home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / pine / pine.c < prev    next >
C/C++ Source or Header  |  1997-02-26  |  79KB  |  2,719 lines

  1. #if !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: pine.c,v 4.335 1996/07/15 20:32:28 mikes Exp $";
  3. #endif
  4. /*----------------------------------------------------------------------
  5.  
  6.             T H E    P I N E    M A I L   S Y S T E M
  7.  
  8.    Laurence Lundblade and Mike Seibel
  9.    Networks and Distributed Computing
  10.    Computing and Communications
  11.    University of Washington
  12.    Administration Builiding, AG-44
  13.    Seattle, Washington, 98195, USA
  14.    Internet: lgl@CAC.Washington.EDU
  15.              mikes@CAC.Washington.EDU
  16.  
  17.    Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  18.  
  19.  
  20.    Pine and Pico are registered trademarks of the University of Washington.
  21.    No commercial use of these trademarks may be made without prior written
  22.    permission of the University of Washington.
  23.  
  24.    Pine, Pico, and Pilot software and its included text are Copyright
  25.    1989-1997 by the University of Washington.
  26.  
  27.    The full text of our legal notices is contained in the file called
  28.    CPYRIGHT, included with this distribution.
  29.  
  30.  
  31.    Pine is in part based on The Elm Mail System:
  32.     ***********************************************************************
  33.     *  The Elm Mail System  -  Revision: 2.13                             *
  34.     *                                                                     *
  35.     *             Copyright (c) 1986, 1987 Dave Taylor              *
  36.     *             Copyright (c) 1988, 1989 USENET Community Trust   *
  37.     ***********************************************************************
  38.  
  39.  
  40.   ----------------------------------------------------------------------*/
  41.  
  42. #include "headers.h"
  43.  
  44.  
  45. /*
  46.  * Handy local definitions...
  47.  */
  48. #define    LEGAL_NOTICE \
  49.    "Copyright 1989-1997.  PINE is a trademark of the University of Washington."
  50.  
  51. #define    STDIN_FD    0            /* Our normal input desc    */
  52. #define    STDER_FD    2            /* Another desc tied to tty */ 
  53. #define    PIPED_FD    5            /* Some innocuous desc        */
  54.  
  55.  
  56. /*
  57.  * Globals referenced throughout pine...
  58.  */
  59. struct pine *ps_global;                /* THE global variable! */
  60. char        *pine_version = PINE_VERSION;    /* version string */
  61.  
  62.  
  63. /*----------------------------------------------------------------------
  64.   General use big buffer. It is used in the following places:
  65.     compose_mail:    while parsing header of postponed message
  66.     append_message2: while writing header into folder
  67.     q_status_messageX: while doing printf formatting
  68.     addr_book: Used to return expanded address in. (Can only use here 
  69.                because mm_log doesn't q_status on PARSE errors !)
  70.     pine.c: When address specified on command line
  71.     init.c: When expanding variable values
  72.  
  73.  ----*/
  74. char         tmp_20k_buf[20480];
  75.  
  76.  
  77. /*
  78.  * byte count used by our gets routine to keep track 
  79.  */
  80. unsigned long gets_bytes;
  81.  
  82.  
  83. /*
  84.  * Internal prototypes
  85.  */
  86. int   setup_mini_menu PROTO((int));
  87. void  do_menu PROTO((int, Pos *));
  88. void  main_redrawer PROTO(());
  89. void  new_user_or_version PROTO((char []));
  90. void  truncated_listvars_warning PROTO((int *));        /** temporary **/
  91. void  phone_home_blurb PROTO((int));
  92. void  do_setup_task PROTO(());
  93. void  queue_init_errors PROTO((struct pine *));
  94. void  upgrade_old_postponed PROTO(());
  95. void  goodnight_gracey PROTO((struct pine *, int));
  96. int   read_stdin_char PROTO(());
  97. char *pine_gets PROTO((readfn_t, void *, unsigned long));
  98. char *nntp_host PROTO((char *));
  99.  
  100.  
  101. #ifdef    DOS
  102. static char first_time_message[] = "\
  103.                    Welcome to PC-Pine...\n\
  104. \n\
  105. a Program for Internet News and Email.  Pine offers the ability to:\n\
  106.   -Access local and remote message folders using a simple user-interface\n\
  107.   -Send documents, graphics, etc (via the MIME standard for attachments)\n\
  108. \n\
  109. COMMANDS IN PINE:  Available commands are always listed on the last\n\
  110.   two lines of the screen.  If there are more than can be displayed, the\n\
  111.   \"O\" command will cycle their display.  Except in function key mode,\n\
  112.   commands can be executed even though they are not displayed.\n\
  113. \n\
  114. PINE CONFIGURATION:  If you haven't yet filled out your Pine configuration\n\
  115.   file, Pine will ask you for this information as it's needed.  For further \n\
  116.   customization, use the Setup/Config Screen (\"S\" then \"C\" in Main Menu).";
  117.  
  118. static char new_version_message[] = "\
  119.              <<<This message will appear only once.>>>\n\
  120. \n\
  121.              Welcome to the latest version of Pine!\n\
  122. \n\
  123. Your Pine configuration file shows that you have not used this version of\n\
  124. Pine before.  You'll see very few changes to Pine's standard behavior, but\n\
  125. there are many new features that you may enable via the new Config\n\
  126. screen under the Main Menu SETUP command.  See the Release Notes\n\
  127. (\"R\" on the Main Menu) for a more detailed list of changes. \n";
  128.  
  129. #else
  130. #ifdef OS2
  131. static char first_time_message[] = "\
  132.                    Welcome to PC-Pine for OS/2...\n\
  133. \n\
  134. a Program for Internet News and Email.  Pine offers the ability to:\n\
  135.   -Access local and remote message folders using a simple user-interface\n\
  136.   -Send documents, graphics, etc (via the MIME standard for attachments)\n\
  137. \n\
  138. COMMANDS IN PINE:  Available commands are always listed on the last\n\
  139.   two lines of the screen.  If there are more than can be displayed, the\n\
  140.   \"O\" command will cycle their display.  Except in function key mode,\n\
  141.   commands can be executed even though they are not displayed.\n\
  142. \n\
  143. PINE CONFIGURATION:  If you haven't yet filled out your Pine configuration\n\
  144.   file, Pine will ask you for this information as it's needed.  For further \n\
  145.   customization, use the Setup/Config Screen (\"S\" then \"C\" in Main Menu).";
  146.  
  147. static char new_version_message[] = "\
  148.              <<<This message will appear only once.>>>\n\
  149. \n\
  150.              Welcome to the latest version of Pine!\n\
  151. \n\
  152. Your Pine configuration file shows that you have not used this version of\n\
  153. Pine before.  You'll see very few changes to Pine's standard behavior, but\n\
  154. there are many new features that you may enable via the new Config\n\
  155. screen under the Main Menu SETUP command.  See the Release Notes\n\
  156. (\"R\" on the Main Menu) for a more detailed list of changes. \n";
  157.  
  158. #else
  159. static char first_time_message[] = "\
  160.                    Welcome to Pine...\n\
  161. \n\
  162. a Program for Internet News and Email.  Pine offers the ability to:\n\
  163.   -Access local and remote message folders using a simple user-interface\n\
  164.   -Send documents, graphics, etc (via the MIME standard for attachments)\n\
  165. \n\
  166. COMMANDS IN PINE:  Available commands are always listed on the last\n\
  167.  two lines of the screen.  If there are more than can be displayed, the\n\
  168.   \"O\" command will cycle their display.  Except in function key mode,\n\
  169.   commands can be executed even though they are not displayed.\n\
  170. \n\
  171. PINE CONFIGURATION:  Pine has created a default configuration file for you.\n\
  172.   To customize pine's behavior, use the Setup/Config (\"S\" then \"C\"\n\
  173.   in Main Menu).  We also suggest seeing pine's main help (\"?\" in Main \
  174. Menu).";
  175.  
  176.  
  177. static char new_version_message[] = "\
  178.              <<<This message will appear only once.>>>\n\
  179. \n\
  180.              Welcome to the latest version of Pine!\n\
  181. \n\
  182. Your Pine configuration file shows that you have not used this version of\n\
  183. Pine before.  You'll see very few changes to Pine's standard behavior, but\n\
  184. there are many new features that you may enable via the new Config\n\
  185. screen under the Main Menu SETUP command.  See the Release Notes\n\
  186. (\"R\" on the Main Menu) for a more detailed list of changes.";
  187. #endif
  188. #endif
  189.  
  190.  
  191.  
  192. static struct key main_keys[] =
  193.        {{"?","Help",KS_SCREENHELP},
  194.     {"O","OTHER CMDS",KS_NONE},
  195.     {NULL,NULL,KS_NONE},
  196.     {NULL,NULL,KS_NONE},
  197.     {"P","PrevCmd",KS_NONE},
  198.     {"N","NextCmd",KS_NONE},
  199.     {NULL,NULL,KS_NONE},
  200.     {NULL,NULL,KS_NONE},
  201.     {"R","RelNotes",KS_NONE},
  202.     {"K","KBLock",KS_NONE},
  203.     {NULL,NULL,KS_NONE},
  204.     {NULL,NULL,KS_NONE},
  205.  
  206.     {"?","Help",KS_SCREENHELP},
  207.     {"O","OTHER CMDS",KS_NONE},
  208.     {"Q","Quit",KS_EXIT},
  209.     {"C","Compose",KS_COMPOSER},
  210.     {"L","ListFldrs",KS_FLDRLIST},
  211.     {"G","GotoFldr",KS_GOTOFLDR},
  212.     {"I","Index",KS_FLDRINDEX},
  213.     {"J","Journal",KS_REVIEW},
  214.     {"S","Setup",KS_NONE},
  215.     {"A","AddrBook",KS_ADDRBOOK},
  216.     {"B","Report Bug",KS_NONE},
  217.     {NULL,NULL,KS_NONE}};
  218. INST_KEY_MENU(main_keymenu, main_keys);
  219. #define MAIN_HELP_KEY        0
  220. #define MAIN_DEFAULT_KEY    3
  221. #define MAIN_KBLOCK_KEY        9
  222. #define MAIN_HELP_KEY2        12
  223. #define MAIN_QUIT_KEY        14
  224. #define MAIN_COMPOSE_KEY    15
  225. #define MAIN_FOLDER_KEY        16
  226. #define MAIN_INDEX_KEY        18
  227. #define MAIN_SETUP_KEY        20
  228. #define MAIN_ADDRESS_KEY    21
  229.  
  230. /*
  231.  * length of longest label from keymenu, of labels corresponding to
  232.  * commands in the middle of the screen.  9 is length of ListFldrs
  233.  */
  234. #define LONGEST_LABEL 9  /* length of longest label from keymenu */
  235. #define LONGEST_NAME 1   /* length of longest name from keymenu */
  236.  
  237.  
  238.  
  239. /*----------------------------------------------------------------------
  240.      main routine -- entry point
  241.  
  242.   Args: argv, argc -- The command line arguments
  243.  
  244.  
  245.  Initialize pine, parse arguments and so on
  246.  
  247.  If there is a user address on the command line go into send mode and exit,
  248.  otherwise loop executing the various screens in Pine.
  249.  
  250.  NOTE: The Windows 3.1 port def's this to "app_main"
  251.   ----*/
  252.  
  253. main(argc, argv)
  254.     int   argc;
  255.     char *argv[];
  256. {
  257.     char            *folder_to_open;
  258.     int              rv;
  259.     struct pine     *pine_state;
  260.     char             int_mail[MAXPATH+1];
  261.     gf_io_t         stdin_getc = NULL;
  262. #ifdef DYN
  263.     char stdiobuf[64];
  264. #endif
  265.  
  266. #ifdef LC_COLLATE
  267.     /*
  268.      * This may not have the desired effect, if strcmp and friends
  269.      * don't know about it.
  270.      */
  271.     setlocale(LC_COLLATE, "");
  272. #endif
  273. #ifdef LC_CTYPE
  274.     setlocale(LC_CTYPE, "");
  275. #endif
  276.  
  277.     /*----------------------------------------------------------------------
  278.           Set up buffering and some data structures
  279.       ----------------------------------------------------------------------*/
  280.  
  281.     pine_state                 = (struct pine *)fs_get(sizeof (struct pine));
  282.     memset((void *)pine_state, 0, sizeof(struct pine));
  283.     ps_global                  = pine_state;
  284.     ps_global->def_sort        = SortArrival;
  285.     ps_global->sort_types[0]   = SortSubject;
  286.     ps_global->sort_types[1]   = SortArrival;
  287.     ps_global->sort_types[2]   = SortFrom;
  288.     ps_global->sort_types[3]   = SortTo;
  289.     ps_global->sort_types[4]   = SortCc;
  290.     ps_global->sort_types[5]   = SortDate;
  291.     ps_global->sort_types[6]   = SortSize;
  292.     ps_global->sort_types[7]   = SortSubject2;
  293.     ps_global->sort_types[8]   = EndofList;
  294.     ps_global->atmts           = (ATTACH_S *) fs_get(sizeof(ATTACH_S));
  295.     ps_global->atmts_allocated = 1;
  296.     ps_global->atmts->description = NULL;
  297.     ps_global->low_speed       = 1;
  298.     ps_global->init_context    = -1;
  299.     mn_init(&ps_global->msgmap, 0L);
  300.     init_init_vars(ps_global);
  301.  
  302. #if !defined(DOS) && !defined(OS2)
  303.     /*
  304.      * Seed the random number generator with the date & pid.  Random 
  305.      * numbers are used for new mail notification and bug report id's
  306.      */
  307.     srandom(getpid() + time(0));
  308. #endif
  309.  
  310. #ifdef DYN
  311.     /*-------------------------------------------------------------------
  312.       There's a bug in DYNIX that causes the terminal driver to lose
  313.       characters when large I/O writes are done on slow lines. Like
  314.       a 1Kb write(2) on a 1200 baud line. Usually CR is output which
  315.       causes a flush before the buffer is too full, some the pine composer
  316.       doesn't output newlines a lot. Either stdio should be fixed to
  317.       continue with more writes when the write request is partial, or
  318.       fix the tty driver to always complete the write.
  319.      */
  320.     setbuffer(stdout, stdiobuf, 64);
  321. #endif
  322.  
  323.     /* need home directory early */
  324.     get_user_info(&ps_global->ui);
  325.     if(getenv("HOME") != NULL)
  326.       pine_state->home_dir = cpystr(getenv("HOME"));
  327.     else
  328.       pine_state->home_dir = cpystr(ps_global->ui.homedir);
  329.  
  330. #if defined(DOS) || defined(OS2)
  331.     {
  332.     char *p;
  333.  
  334.     /* normalize path delimiters */
  335.     for(p = pine_state->home_dir; p = strchr(p, '/'); p++)
  336.       *p='\\';
  337.     }
  338. #endif
  339.  
  340.     /*----------------------------------------------------------------------
  341.            Parse arguments and initialize debugging
  342.       ----------------------------------------------------------------------*/
  343.     folder_to_open = pine_args(pine_state, argc, argv, &argv);
  344.  
  345. #ifndef    _WINDOWS
  346.     if(!isatty(0)){
  347.     /*
  348.      * monkey with descriptors so our normal tty i/o routines don't
  349.      * choke...
  350.      */
  351.     dup2(STDIN_FD, PIPED_FD);    /* redirected stdin to new desc */
  352.     dup2(STDER_FD, STDIN_FD);    /* rebind stdin to the tty    */
  353.     stdin_getc = read_stdin_char;
  354.     }
  355. #endif
  356.  
  357. #ifdef DEBUG
  358.     /* Since this is specific debugging we don't mind if the
  359.        ifdef is the type of system. See conf/templets.h
  360.      */
  361. #ifdef HAVE_SMALLOC 
  362.     if(debug > 8)
  363.       malloc_debug(2);
  364. #endif
  365. #ifdef NXT
  366.     if(debug > 8)
  367.       malloc_debug(32); 
  368. #endif
  369. #ifdef    CSRIMALLOC
  370.     mal_debug((debug <= DEFAULT_DEBUG) ? 1 : (debug < 9) ? 2 : 3);
  371. #endif
  372.  
  373.     init_debug();
  374.  
  375. #ifdef    _WINDOWS
  376.     mswin_setdebug(debug, debugfile);
  377. #endif
  378. #endif  /* DEBUG */
  379.  
  380.     /*------- Set up c-client drivers -------*/ 
  381. #include "../c-client/linkage.c"
  382.  
  383.     /*------- Tune the drivers just installed -------*/ 
  384. #if    defined(DOS) && !defined(WIN32)
  385.     /*
  386.      * install c-client callback to manage cache data outside
  387.      * free memory
  388.      */
  389.     mail_parameters(NULL, SET_CACHE, (void *)dos_cache);
  390.  
  391.     /*
  392.      * Sniff the environment for timezone offset.  We need to do this
  393.      * here since Windows needs help figuring out UTC, and will adjust
  394.      * what time() returns based on TZ.  THIS WILL SCREW US because
  395.      * we use time() differences to manage status messages.  So, if 
  396.      * rfc822_date, which calls localtime() and thus needs tzset(),
  397.      * is called while a status message is displayed, it's possible
  398.      * for time() to return a time *before* what we remember as the
  399.      * time we put the status message on the display.  Sheesh.
  400.      */
  401.     tzset();
  402. #else
  403.     /*
  404.      * Install our own gets routine so we can count the bytes read
  405.      * during a fetch...
  406.      */
  407.     (void) mail_parameters(NULL, SET_GETS, (void *)pine_gets);
  408. #endif
  409.     /*
  410.      * Give c-client a buffer to read "/user=" option names into.
  411.      */
  412.     (void) mail_parameters(NULL, SET_USERNAMEBUF, (void *) fs_get(256));
  413.  
  414.     init_vars(pine_state);
  415.  
  416.     /*
  417.      * Set up a c-client read timeout and timeout handler.  In general,
  418.      * it shouldn't happen, but a server crash or dead link can cause
  419.      * pine to appear wedged if we don't set this up...
  420.      */
  421.     mail_parameters(NULL, SET_OPENTIMEOUT,
  422.             (void *)((pine_state->VAR_TCPOPENTIMEO
  423.                   && (rv = atoi(pine_state->VAR_TCPOPENTIMEO)) > 4)
  424.                    ? (long) rv : 30L));
  425.     mail_parameters(NULL, SET_READTIMEOUT, (void *) 15L);
  426.     mail_parameters(NULL, SET_TIMEOUT, (void *) pine_tcptimeout);
  427.     if(pine_state->VAR_RSHOPENTIMEO
  428.     && ((rv = atoi(pine_state->VAR_RSHOPENTIMEO)) == 0 || rv > 4))
  429.       mail_parameters(NULL, SET_RSHTIMEOUT, (void *) rv);
  430.  
  431.     if(init_username(pine_state) < 0)
  432.       exit(-1);
  433.  
  434.     if(init_hostname(pine_state) < 0)
  435.       exit(-1);
  436.  
  437.     if(!pine_state->nr_mode)
  438.       write_pinerc(pine_state);
  439.  
  440.     /*
  441.      * Verify mail dir if we're not in send only mode...
  442.      */
  443.     if(!pine_state->more_mode && *argv == NULL
  444.     && init_mail_dir(pine_state) < 0)
  445.       exit(-1);
  446.  
  447.     init_signals();
  448.  
  449.     /*--- input side ---*/
  450.     if(init_tty_driver(pine_state)){
  451. #if !defined(DOS) && !defined(OS2)    /* always succeeds under DOS! */
  452.         fprintf(stderr, "Can't access terminal or input is not a terminal. ");
  453.         fprintf(stderr, "Redirection of\nstandard input is not allowed. For ");
  454.         fprintf(stderr, "example \"pine < file\" doesn't work.\n%c", BELL);
  455.         exit(-1);
  456. #endif
  457.     }
  458.         
  459.  
  460.     /*--- output side ---*/
  461.     rv = config_screen(&(pine_state->ttyo), &(pine_state->kbesc));
  462. #if !defined(DOS) && !defined(OS2)    /* always succeeds under DOS! */
  463.     if(rv){
  464.         switch(rv){
  465.           case -1:
  466.         printf("Terminal type (environment variable TERM) not set.\n");
  467.             break;
  468.           case -2:
  469.         printf("Terminal type \"%s\", is unknown.\n", getenv("TERM"));
  470.             break;
  471.           case -3:
  472.             printf("Can't open termcap file; check TERMCAP");
  473.         printf(" variable and/or system manager.\n");
  474.             break;
  475.           case -4:
  476.             printf("Your terminal, of type \"%s\",", getenv("TERM"));
  477.         printf(" is lacking functions needed to run pine.\n");
  478.             break;
  479.         }
  480.  
  481.         printf("\r");
  482.         end_tty_driver(pine_state);
  483.         exit(-1);
  484.     }
  485. #endif
  486.  
  487.     if(F_ON(F_BLANK_KEYMENU,ps_global))
  488.       FOOTER_ROWS(ps_global) = 1;
  489.  
  490.     init_screen();
  491.     init_keyboard(pine_state->orig_use_fkeys);
  492.     strcpy(pine_state->inbox_name, INBOX_NAME);
  493.     init_folders(pine_state);        /* digest folder spec's */
  494.  
  495.     pine_state->in_init_seq = 0;    /* so output (& ClearScreen) show up */
  496.     pine_state->dont_use_init_cmds = 1;    /* don't use up initial_commands yet */
  497.     ClearScreen();
  498.     if(!pine_state->more_mode
  499.        && (pine_state->first_time_user || pine_state->show_new_version)){
  500.     pine_state->mangled_header = 1;
  501.     show_main_screen(pine_state, 0, FirstMenu, NULL, 0, (Pos *)NULL);
  502.     if(!pine_state->nr_mode){
  503.         if(pine_state->first_time_user)
  504.           new_user_or_version(first_time_message);
  505.         else
  506.           new_user_or_version(new_version_message);
  507.     }
  508.  
  509.     ClearScreen();
  510.     }
  511.     
  512.     /* put back in case we need to suppress output */
  513.     pine_state->in_init_seq = pine_state->save_in_init_seq;
  514.  
  515.     /* queue any init errors so they get displayed in a screen below */
  516.     queue_init_errors(ps_global);
  517.  
  518.     if(pine_state->more_mode){        /* "Page" the given file */
  519.     int dice = 1, redir = 0;
  520.  
  521.     if(pine_state->in_init_seq){
  522.         pine_state->in_init_seq = pine_state->save_in_init_seq = 0;
  523.         clear_cursor_pos();
  524.         if(pine_state->free_initial_cmds)
  525.           fs_give((void **)&(pine_state->free_initial_cmds));
  526.  
  527.         pine_state->initial_cmds = 0;
  528.     }
  529.  
  530.     /*======= Requested that we simply page the given folder =======*/
  531.     if(folder_to_open){        /* Open the requested folder... */
  532.         SourceType  src;
  533.         STORE_S    *store = NULL;
  534.         char       *decode_error = NULL;
  535.  
  536.         if(stdin_getc){
  537.         redir++;
  538.         src = CharStar;
  539.         if(isatty(0) && (store = so_get(src, NULL, EDIT_ACCESS))){
  540.             gf_io_t pc;
  541.  
  542.             gf_set_so_writec(&pc, store);
  543.             gf_filter_init();
  544.             if(decode_error = gf_pipe(stdin_getc, pc)){
  545.             dice = 0;
  546.             q_status_message1(SM_ORDER, 3, 4,
  547.                       "Problem reading stdin: %s",
  548.                       decode_error);
  549.             }
  550.         }
  551.         else
  552.           dice = 0;
  553.         }
  554.         else{
  555.         src = FileStar;
  556.         strcpy(ps_global->cur_folder, folder_to_open);
  557.         if((store = so_get(src, folder_to_open, READ_ACCESS)) == NULL)
  558.           dice = 0;
  559.         }
  560.  
  561.         if(dice){
  562.         scrolltool((void *)so_text(store), "FILE VIEW", SimpleText,
  563.                src, NULL);
  564.         printf("\n\n");
  565.         so_give(&store);
  566.         }
  567.     }
  568.  
  569.     if(!dice){
  570.         q_status_message2(SM_ORDER, 3, 4, "Can't display \"%s\": %s",
  571.          (redir) ? "Standard Input" 
  572.              : folder_to_open ? folder_to_open : "NULL",
  573.          error_description(errno));
  574.     }
  575.  
  576.     goodnight_gracey(pine_state, 0);
  577.     }
  578.     else if(*argv || stdin_getc){    /* send mail using given input */
  579.         /*======= address on command line/send one message mode ============*/
  580.         char *to, **t, *error = NULL, *addr;
  581.         int   len, good_addr = 1;
  582.     int   exit_val;
  583.     BUILDER_ARG fcc;
  584.  
  585.     if(pine_state->in_init_seq){
  586.         pine_state->in_init_seq = pine_state->save_in_init_seq = 0;
  587.         clear_cursor_pos();
  588.         if(pine_state->free_initial_cmds)
  589.           fs_give((void **)&(pine_state->free_initial_cmds));
  590.  
  591.         pine_state->initial_cmds = 0;
  592.     }
  593.  
  594.         /*----- Format the To: line with commas for the composer ---*/
  595.     if(argv){
  596.         for(t = argv, len = 0; *t != NULL; len += strlen(*t++) + 2)
  597.           ;/* do nothing */
  598.  
  599.         to = fs_get(len + 5);
  600.         to[0] = '\0';
  601.         for(t = argv, len = 0; *t != NULL; t++){
  602.         if(to[0] != '\0')
  603.           strcat(to, ", ");
  604.  
  605.         strcat(to, *t);
  606.         }
  607.  
  608.         fcc.tptr = NULL;
  609.         fcc.next = NULL;
  610.         fcc.xtra = NULL;
  611.         good_addr = (build_address(to, &addr, &error, &fcc) >= 0);
  612.     }
  613.  
  614.     if(good_addr)
  615.       compose_mail(addr, fcc.tptr, stdin_getc);
  616.  
  617.     if(addr)
  618.       fs_give((void **)&addr);
  619.  
  620.     if(fcc.tptr)
  621.       fs_give((void **)&fcc.tptr);
  622.  
  623.         fs_give((void **)&to);
  624.     if(!good_addr){
  625.         char buf[500];
  626.  
  627.         q_status_message1(SM_ORDER, 3, 4, "Bad address: %s", error);
  628.         exit_val = -1;
  629.     }
  630.     else
  631.       exit_val = 0;
  632.  
  633.     if(error)
  634.       fs_give((void **)&error);
  635.  
  636.     goodnight_gracey(pine_state, exit_val);
  637.     }
  638.     else{
  639.         struct key_menu *km = &main_keymenu;
  640.  
  641.         /*========== Normal pine mail reading mode ==========*/
  642.             
  643.         pine_state->mail_stream    = NULL;
  644.         pine_state->inbox_stream   = NULL;
  645.         pine_state->mangled_screen = 1;
  646.     
  647.         if(!pine_state->start_in_index){
  648.         /* flash message about executing initial commands */
  649.         if(pine_state->in_init_seq){
  650.             pine_state->in_init_seq    = 0;
  651.         clear_cursor_pos();
  652.         pine_state->mangled_header = 1;
  653.         pine_state->mangled_footer = 1;
  654.         pine_state->mangled_screen = 0;
  655.         /* show that this is Pine */
  656.         show_main_screen(pine_state, 0, FirstMenu, km, 0, (Pos *)NULL);
  657.         pine_state->mangled_screen = 1;
  658.         pine_state->painted_footer_on_startup = 1;
  659.         if(min(4, pine_state->ttyo->screen_rows - 4) > 1)
  660.               PutLine0(min(4, pine_state->ttyo->screen_rows - 4),
  661.             max(min(11, pine_state->ttyo->screen_cols -38), 0),
  662.             "Executing initial-keystroke-list......");
  663.  
  664.             pine_state->in_init_seq = 1;
  665.         }
  666.         else{
  667.                 show_main_screen(pine_state, 0, FirstMenu, km, 0, (Pos *)NULL);
  668.         pine_state->painted_body_on_startup   = 1;
  669.         pine_state->painted_footer_on_startup = 1;
  670.         }
  671.         }
  672.     else{
  673.         /* cancel any initial commands, overridden by cmd line */
  674.         if(pine_state->in_init_seq){
  675.         pine_state->in_init_seq      = 0;
  676.         pine_state->save_in_init_seq = 0;
  677.         clear_cursor_pos();
  678.         if(pine_state->initial_cmds){
  679.             if(pine_state->free_initial_cmds)
  680.               fs_give((void **)&(pine_state->free_initial_cmds));
  681.  
  682.             pine_state->initial_cmds = 0;
  683.         }
  684.  
  685.         F_SET(F_USE_FK,pine_state, pine_state->orig_use_fkeys);
  686.         }
  687.  
  688.             do_index_border(pine_state->context_current,
  689.                 pine_state->cur_folder, pine_state->mail_stream,
  690.                 pine_state->msgmap, MsgIndex, NULL,
  691.                 INDX_CLEAR|INDX_HEADER|INDX_FOOTER);
  692.         pine_state->painted_footer_on_startup = 1;
  693.         if(min(4, pine_state->ttyo->screen_rows - 4) > 1)
  694.           PutLine1(min(4, pine_state->ttyo->screen_rows - 4),
  695.         max(min(11, pine_state->ttyo->screen_cols -40), 0),
  696.         "Please wait, opening %s......",
  697.          pine_state->nr_mode ? "news messages" : "mail folder");
  698.         }
  699.  
  700.         fflush(stdout);
  701.  
  702.     if(pine_state->in_init_seq){
  703.         pine_state->in_init_seq = 0;
  704.         clear_cursor_pos();
  705.     }
  706.  
  707.         if(folder_to_open != NULL){
  708.         CONTEXT_S *cntxt;
  709.  
  710.         if((rv = pine_state->init_context) < 0)
  711.           cntxt = pine_state->context_current;
  712.         else if(rv == 0)
  713.           cntxt = NULL;
  714.         else
  715.           for(cntxt = pine_state->context_list;
  716.           rv > 1 && cntxt->next;
  717.           rv--, cntxt = cntxt->next)
  718.         ;
  719.  
  720.             if(do_broach_folder(folder_to_open, cntxt) <= 0){
  721.         q_status_message2(SM_ORDER, 3, 4,
  722.             "Unable to open %s \"%s\"",
  723.             pine_state->nr_mode ? "news messages" : "folder",
  724.             folder_to_open);
  725.  
  726.         goodnight_gracey(pine_state, -1);
  727.             }
  728.         }
  729.     else{
  730. #if defined(DOS) || defined(OS2)
  731.             /*
  732.          * need to ask for the inbox name if no default under DOS
  733.          * since there is no "inbox"
  734.          */
  735.  
  736.         if(!pine_state->VAR_INBOX_PATH || !pine_state->VAR_INBOX_PATH[0]
  737.            || strucmp(pine_state->VAR_INBOX_PATH, "inbox") == 0){
  738.         HelpType help = NO_HELP;
  739.         static   ESCKEY_S ekey[] = {{ctrl(T), 2, "^T", "To Fldrs"},
  740.                       {-1, 0, NULL, NULL}};
  741.  
  742.         pine_state->mangled_footer = 1;
  743.         int_mail[0] = '\0';
  744.             while(1){
  745.                 rv = optionally_enter(int_mail, -FOOTER_ROWS(pine_state),
  746.                       0, MAXPATH, 1, 0,
  747.                       "No inbox!  Folder to open as inbox : ",
  748.                       ekey, help, 0);
  749.                 if(rv == 3){
  750.             help = (help == NO_HELP) ? h_sticky_inbox : NO_HELP;
  751.             continue;
  752.             }
  753.  
  754.                 if(rv != 4)
  755.               break;
  756.             }
  757.  
  758.             if(rv == 1){
  759.             q_status_message(SM_ORDER, 0, 2 ,"Folder open cancelled");
  760.             rv = 0;        /* reset rv */
  761.         } 
  762.         else if(rv == 2){
  763.                 if(!folder_lister(pine_state, OpenFolder, NULL,
  764.                    &(pine_state->context_current),
  765.                    int_mail, NULL,
  766.                    pine_state->context_current, NULL))
  767.               *int_mail = '\0';    /* user cancelled! */
  768.  
  769.                     show_main_screen(pine_state,0,FirstMenu,km,0,(Pos *)NULL);
  770.         }
  771.  
  772.         if(*int_mail){
  773.             removing_trailing_white_space(int_mail);
  774.             removing_leading_white_space(int_mail);
  775.             if((!pine_state->VAR_INBOX_PATH 
  776.             || strucmp(pine_state->VAR_INBOX_PATH, "inbox") == 0)
  777.              && want_to("Preserve folder as \"inbox-path\" in PINERC", 
  778.                 'y', 'n', NO_HELP, 0, 0) == 'y'){
  779.             set_variable(V_INBOX_PATH, int_mail, 1);
  780.             }
  781.             else{
  782.             if(pine_state->VAR_INBOX_PATH)
  783.               fs_give((void **)&pine_state->VAR_INBOX_PATH);
  784.  
  785.             pine_state->VAR_INBOX_PATH = cpystr(int_mail);
  786.             }
  787.  
  788.             do_broach_folder(pine_state->inbox_name, 
  789.                      pine_state->context_list);
  790.             }
  791.         else
  792.           q_status_message(SM_ORDER, 0, 2 ,"No folder opened");
  793.  
  794.         }
  795.         else
  796.  
  797. #endif
  798.             do_broach_folder(pine_state->inbox_name, pine_state->context_list);
  799.         }
  800.  
  801.         if(pine_state->mangled_footer)
  802.       pine_state->painted_footer_on_startup = 0;
  803.  
  804.         if(!pine_state->nr_mode
  805.        && pine_state->mail_stream
  806.        && expire_sent_mail())
  807.       pine_state->painted_footer_on_startup = 0;
  808.  
  809.     /*
  810.      * Initialize the defaults.  Initializing here means that
  811.      * if they're remote, the user isn't prompted for an imap login
  812.      * before the display's drawn, AND there's the chance that
  813.      * we can climb onto the already opened folder's stream...
  814.      */
  815.     if(ps_global->first_time_user || ps_global->show_new_version)
  816.       init_save_defaults();    /* initialize default save folders */
  817.  
  818.     build_path(int_mail,
  819.            ps_global->VAR_OPER_DIR ? ps_global->VAR_OPER_DIR
  820.                        : pine_state->home_dir,
  821.            INTERRUPTED_MAIL);
  822.     if(!pine_state->nr_mode && folder_exists("[]", int_mail) > 0)
  823.       q_status_message(SM_ORDER | SM_DING, 4, 5, 
  824.                "Use compose command to continue interrupted message.");
  825.  
  826. #if defined(USE_QUOTAS)
  827.     {
  828.         long q;
  829.         int  over;
  830.         q = disk_quota(pine_state->home_dir, &over);
  831.         if(q > 0 && over){
  832.         q_status_message2(SM_ASYNC | SM_DING, 4, 5,
  833.                   "WARNING! Over your disk quota by %s bytes (%s)",
  834.                   comatose(q),byte_string(q));
  835.         }
  836.     }
  837. #endif
  838.  
  839.     pine_state->in_init_seq = pine_state->save_in_init_seq;
  840.     pine_state->dont_use_init_cmds = 0;
  841.     clear_cursor_pos();
  842.  
  843.     if(pine_state->give_fixed_warning)
  844.       q_status_message(SM_ASYNC, 0, 10,
  845. "Note: some of your config options conflict with site policy and are ignored");
  846.  
  847.     if(timeout == 0 &&
  848.        ps_global->VAR_INBOX_PATH &&
  849.        ps_global->VAR_INBOX_PATH[0] == '{')
  850.       q_status_message(SM_ASYNC, 0, 10,
  851. "Note: mail-check-interval=0 may cause IMAP server connection to time out");
  852.  
  853.         /*-------------------------------------------------------------------
  854.                          Loop executing the commands
  855.     
  856.             This is done like this so that one command screen can cause
  857.             another one to execute it with out going through the main menu. 
  858.           ------------------------------------------------------------------*/
  859.         pine_state->next_screen = pine_state->start_in_index ?
  860.                                          mail_index_screen : 
  861.                                          main_menu_screen;
  862.         while(1){
  863.             if(pine_state->next_screen == SCREEN_FUN_NULL) 
  864.               pine_state->next_screen = main_menu_screen;
  865.  
  866.             (*(pine_state->next_screen))(pine_state);
  867.         }
  868.     }
  869. }
  870.  
  871.  
  872.  
  873. /*
  874.  * read_stdin_char - simple function to return a character from
  875.  *             redirected stdin
  876.  */
  877. int
  878. read_stdin_char(c)
  879.     char *c;
  880. {
  881.     /* it'd probably be a good idea to fix this to pre-read blocks */
  882.     return(read(PIPED_FD, c, 1) == 1);
  883. #ifdef    notdef
  884.  
  885.             gf_io_t pc;
  886.             char    bigbuf[1025];
  887.             int     i;
  888.  
  889.             strcpy(ps_global->cur_folder, "Standard-Input");
  890.             gf_set_so_writec(&pc, store);
  891.             while((i = read(PIPED_FD, bigbuf, 1024)) > 0){
  892.             bigbuf[i] = '\0';
  893.             gf_puts(bigbuf, pc);
  894.             }
  895.  
  896.             if(i < 0)        /* Bummer. */
  897.               dice = 0;
  898. #endif
  899. }
  900.  
  901.  
  902. /* this default is from the array of structs below */
  903. #define DEFAULT_MENU_ITEM 6        /* LIST FOLDERS */
  904. #define MAX_DEFAULT_MENU_ITEM 12
  905. #define UNUSED 0
  906. static unsigned char current_default_menu_item = DEFAULT_MENU_ITEM;
  907.  
  908. /*
  909.  * One of these for each line that gets printed in the middle of the
  910.  * screen in the main menu.
  911.  */
  912. static struct menu_key {
  913.     char         *key_and_name,
  914.          *news_addition;
  915.     unsigned int f_key;           /* function key that invokes this action */
  916.     unsigned int key;             /* alpha key that invokes this action */
  917.     unsigned int keymenu_number;  /* index into keymenu array for this cmd */
  918. } mkeys[] = {
  919.     {" %s     HELP               -  Get help using Pine",
  920.        NULL, PF1, '?', MAIN_HELP_KEY},
  921.     {"", NULL, UNUSED, UNUSED, UNUSED},
  922.     {" %s     COMPOSE MESSAGE    -  Compose and send%s a message",
  923.        "/post", OPF4, 'C', MAIN_COMPOSE_KEY},
  924.     {"", NULL, UNUSED, UNUSED, UNUSED},
  925.     {" %s     FOLDER INDEX       -  View messages in current folder",
  926.        NULL, OPF7, 'I', MAIN_INDEX_KEY},
  927.     {"", NULL, UNUSED, UNUSED, UNUSED},
  928.     {" %s     FOLDER LIST        -  Select a folder%s to view",
  929.        " OR news group", OPF5, 'L', MAIN_FOLDER_KEY},
  930.     {"", NULL, UNUSED, UNUSED, UNUSED},
  931.     {" %s     ADDRESS BOOK       -  Update address book",
  932.        NULL, OPF10, 'A', MAIN_ADDRESS_KEY},
  933.     {"", NULL, UNUSED, UNUSED, UNUSED},
  934.     {" %s     SETUP              -  Configure or update Pine",
  935.        NULL, OPF9, 'S', MAIN_SETUP_KEY},
  936.     {"", NULL, UNUSED, UNUSED, UNUSED},
  937.     {" %s     QUIT               -  Exit the Pine program",
  938.        NULL, OPF3, 'Q', MAIN_QUIT_KEY},
  939.     {NULL, NULL, UNUSED, UNUSED, UNUSED}
  940. };
  941.  
  942.  
  943.  
  944. /*----------------------------------------------------------------------
  945.       display main menu and execute main menu commands
  946.  
  947.     Args: The usual pine structure
  948.  
  949.   Result: main menu commands are executed
  950.  
  951.  
  952.               M A I N   M E N U    S C R E E N
  953.  
  954.    Paint the main menu on the screen, get the commands and either execute
  955. the function or pass back the name of the function to execute for the menu
  956. selection. Only simple functions that always return here can be executed
  957. here.
  958.  
  959. This functions handling of new mail, redrawing, errors and such can 
  960. serve as a template for the other screen that do much the same thing.
  961.  
  962. There is a loop that fetchs and executes commands until a command to leave
  963. this screen is given. Then the name of the next screen to display is
  964. stored in next_screen member of the structure and this function is exited
  965. with a return.
  966.  
  967. First a check for new mail is performed. This might involve reading the new
  968. mail into the inbox which might then cause the screen to be repainted.
  969.  
  970. Then the general screen painting is done. This is usually controlled
  971. by a few flags and some other position variables. If they change they
  972. tell this part of the code what to repaint. This will include cursor
  973. motion and so on.
  974.   ----*/
  975. void
  976. main_menu_screen(pine_state)
  977.     struct pine *pine_state;
  978. {
  979.     int            ch, orig_ch, setup_command,
  980.             just_a_navigate_cmd, km_popped;
  981.     char            *new_folder;
  982.     CONTEXT_S       *tc;
  983.     struct key_menu *km;
  984.     OtherMenu        what;
  985.     Pos              curs_pos;
  986. #if defined(DOS) || defined(OS2)
  987.     extern void (*while_waiting)();
  988. #endif
  989.  
  990.     ps_global                 = pine_state;
  991.     just_a_navigate_cmd       = 0;
  992.     km_popped              = 0;
  993.     current_default_menu_item = DEFAULT_MENU_ITEM;
  994.     what                      = FirstMenu;  /* which keymenu to display */
  995.     ch                        = 'x'; /* For display_message 1st time through */
  996.     pine_state->prev_screen   = main_menu_screen;
  997.     curs_pos.row = pine_state->ttyo->screen_rows-FOOTER_ROWS(pine_state);
  998.     curs_pos.col = 0;
  999.  
  1000.     mailcap_free(); /* free resources we won't be using for a while */
  1001.  
  1002.     if(!pine_state->painted_body_on_startup 
  1003.        && !pine_state->painted_footer_on_startup){
  1004.     pine_state->mangled_screen = 1;
  1005.     }
  1006.     else{
  1007.     /* need cursor position if not drawing */
  1008.     char buf[MAX_SCREEN_COLS+1];
  1009.  
  1010.     /* This only works because default is the longest one */
  1011.     sprintf(buf, mkeys[current_default_menu_item].key_and_name,
  1012.         F_ON(F_USE_FK,ps_global)
  1013.           ? "" : pretty_command(mkeys[current_default_menu_item].key),
  1014.         (ps_global->VAR_NEWS_SPEC &&
  1015.             mkeys[current_default_menu_item].news_addition)
  1016.           ? mkeys[current_default_menu_item].news_addition : "");
  1017.     curs_pos.col = max(((ps_global->ttyo->screen_cols-strlen(buf))/2)-1, 0);
  1018.     curs_pos.col += 6;
  1019.     if(F_OFF(F_USE_FK,ps_global))
  1020.       curs_pos.col++;
  1021.     
  1022.     curs_pos.col = min(ps_global->ttyo->screen_cols-1, curs_pos.col);
  1023.     curs_pos.row = current_default_menu_item + 3;
  1024.     }
  1025.  
  1026.     km = &main_keymenu;
  1027.  
  1028.     dprint(1, (debugfile, "\n\n    ---- MAIN_MENU_SCREEN ----\n"));
  1029.  
  1030.     while(1){
  1031.     if(km_popped){
  1032.         km_popped--;
  1033.         if(km_popped == 0){
  1034.         clearfooter(pine_state);
  1035.         pine_state->mangled_body = 1;
  1036.         }
  1037.     }
  1038.  
  1039.     /*
  1040.      * fix up redrawer just in case some submenu caused it to get
  1041.      * reassigned...
  1042.      */
  1043.     pine_state->redrawer = main_redrawer;
  1044.  
  1045.     /*----------- Check for new mail -----------*/
  1046.         if(new_mail(0, NM_TIMING(ch), 1) >= 0)
  1047.           pine_state->mangled_header = 1;
  1048.  
  1049.         if(streams_died())
  1050.           pine_state->mangled_header = 1;
  1051.  
  1052.         show_main_screen(pine_state, just_a_navigate_cmd, what, km,
  1053.              km_popped, &curs_pos);
  1054.         just_a_navigate_cmd = 0;
  1055.     what = SameTwelve;
  1056.  
  1057.     /*---- This displays new mail notification, or errors ---*/
  1058.     if(km_popped){
  1059.         FOOTER_ROWS(pine_state) = 3;
  1060.         mark_status_dirty();
  1061.     }
  1062.  
  1063.         display_message(ch);
  1064.     if(km_popped){
  1065.         FOOTER_ROWS(pine_state) = 1;
  1066.         mark_status_dirty();
  1067.     }
  1068.  
  1069.     if(F_OFF(F_SHOW_CURSOR, ps_global)){
  1070.         curs_pos.row =pine_state->ttyo->screen_rows-FOOTER_ROWS(pine_state);
  1071.         curs_pos.col =0;
  1072.     }
  1073.  
  1074.         MoveCursor(curs_pos.row, curs_pos.col);
  1075.  
  1076.         /*------ Read the command from the keyboard ----*/      
  1077. #ifdef    MOUSE
  1078.     mouse_in_content(KEY_MOUSE, -1, -1, 0, 0);
  1079.     register_mfunc(mouse_in_content, HEADER_ROWS(pine_state), 0,
  1080.             pine_state->ttyo->screen_rows-(FOOTER_ROWS(pine_state)+1),
  1081.                pine_state->ttyo->screen_cols);
  1082. #endif
  1083. #if defined(DOS) || defined(OS2)
  1084.     /*
  1085.      * AND pre-build header lines.  This works just fine under
  1086.      * DOS since we wait for characters in a loop. Something will
  1087.          * will have to change under UNIX if we want to do the same.
  1088.      */
  1089.     while_waiting = build_header_cache;
  1090. #endif
  1091.         ch = read_command();
  1092. #ifdef    MOUSE
  1093.     clear_mfunc(mouse_in_content);
  1094. #endif
  1095. #if defined(DOS) || defined(OS2)
  1096.     while_waiting = NULL;
  1097. #endif
  1098.         orig_ch = ch;
  1099.  
  1100.         if(ch < 0x0100 && isupper((unsigned char)ch))
  1101.           ch = tolower((unsigned char)ch);
  1102.     else if(ch >= PF1 && ch <= PF12 && km->which == 1)
  1103.       ch = PF2OPF(ch);
  1104.  
  1105.     /*----- Validate the command ----*/
  1106.     if(ch == ctrl('M') || ch == ctrl('J') || ch == PF4){
  1107.       ch = F_ON(F_USE_FK,pine_state)
  1108.                                        ? mkeys[current_default_menu_item].f_key
  1109.                                        : mkeys[current_default_menu_item].key;
  1110.           if(ch <= 0xff && isupper((unsigned char)ch))
  1111.             ch = tolower((unsigned char)ch);
  1112.     }
  1113.  
  1114.  
  1115.         /*
  1116.      * 'q' is always valid as a way to exit pine even in function key mode
  1117.          */
  1118.         if(ch != 'q')
  1119.           ch = validatekeys(ch);
  1120.  
  1121.     if(km_popped)
  1122.       switch(ch){
  1123.         case NO_OP_IDLE:
  1124.         case NO_OP_COMMAND: 
  1125.         case PF2:
  1126.         case OPF2:
  1127.             case 'o' :
  1128.         case KEY_RESIZE:
  1129.         case ctrl('L'):
  1130.           km_popped++;
  1131.           break;
  1132.         
  1133.         default:
  1134.           clearfooter(pine_state);
  1135.           break;
  1136.       }
  1137.  
  1138.     /*------ Execute the command ------*/
  1139.     switch (ch){
  1140. #if    defined(DOS) && !defined(_WINDOWS)
  1141. /* while we're testing DOS */
  1142.       case 'h': 
  1143.         {
  1144. #include <malloc.h>
  1145.         int    heapresult;
  1146.         int    totalused = 0;
  1147.         int    totalfree = 0;
  1148.         long   totalusedbytes = 0L;
  1149.         long   totalfreebytes = 0L;
  1150.         long   largestinuse = 0L;
  1151.         long   largestfree = 0L, freeaccum = 0L;
  1152.  
  1153.         _HEAPINFO hinfo;
  1154.         extern long coreleft();
  1155.         extern void dumpmetacache();
  1156.  
  1157.         hinfo._pentry = NULL;
  1158.         while((heapresult = _heapwalk(&hinfo)) == _HEAPOK){
  1159.             if(hinfo._useflag == _USEDENTRY){
  1160.             totalused++;
  1161.             totalusedbytes += (long)hinfo._size; 
  1162.             if(largestinuse < (long)hinfo._size)
  1163.               largestinuse = (long)hinfo._size;
  1164.             }
  1165.             else{
  1166.             totalfree++;
  1167.             totalfreebytes += (long)hinfo._size;
  1168.             }
  1169.  
  1170.             if(hinfo._useflag == _USEDENTRY){
  1171.             if(freeaccum > largestfree) /* remember largest run */
  1172.               largestfree = freeaccum;
  1173.             
  1174.             freeaccum = 0L;
  1175.             }
  1176.             else
  1177.               freeaccum += (long)hinfo._size;
  1178.         }
  1179.  
  1180.         sprintf(tmp_20k_buf,
  1181.           "use: %d (%ld, %ld lrg), free: %d (%ld, %ld lrg), DOS: %ld", 
  1182.             totalused, totalusedbytes, largestinuse,
  1183.             totalfree, totalfreebytes, largestfree, coreleft());
  1184.         q_status_message(SM_ORDER, 5, 7, tmp_20k_buf);
  1185.  
  1186.         switch(heapresult/* = _heapchk()*/){
  1187.           case _HEAPBADPTR:
  1188.             q_status_message(SM_ORDER | SM_DING, 1, 2,
  1189.                      "ERROR - Bad ptr in heap");
  1190.             break;
  1191.           case _HEAPBADBEGIN:
  1192.             q_status_message(SM_ORDER | SM_DING, 1, 2,
  1193.                      "ERROR - Bad start of heap");
  1194.             break;
  1195.           case _HEAPBADNODE:
  1196.             q_status_message(SM_ORDER | SM_DING, 1, 2,
  1197.                      "ERROR - Bad node in heap");
  1198.             break;
  1199.           case _HEAPEMPTY:
  1200.             q_status_message(SM_ORDER, 1, 2, "Heap OK - empty");
  1201.             break;
  1202.           case _HEAPEND:
  1203.             q_status_message(SM_ORDER, 1, 2, "Heap checks out!");
  1204.             break;
  1205.           case _HEAPOK:
  1206.             q_status_message(SM_ORDER, 1, 2, "Heap checks out!");
  1207.             break;
  1208.           default:
  1209.             q_status_message1(SM_ORDER | SM_DING, 1, 2,
  1210.                       "BS from heapchk: %d",
  1211.                       (void *)heapresult);
  1212.             break;
  1213.         }
  1214.  
  1215.         /*       dumpmetacache(ps_global->mail_stream);*/
  1216.         /* DEBUG: heapcheck() */
  1217.         /*       q_status_message1(SM_ORDER, 1, 3,
  1218.                          " * * There's %ld bytes of core left for Pine * * ", 
  1219.                          (void *)coreleft());*/
  1220.         }
  1221.         break;
  1222. #endif    /* DOS for testing */
  1223.  
  1224.       /*---------- help ------*/
  1225.       case PF1:
  1226.       case OPF1:
  1227.       case ctrl('G'):
  1228.       case '?':
  1229.       help_case :
  1230.         if(FOOTER_ROWS(pine_state) == 1 && km_popped == 0){
  1231.         km_popped = 2;
  1232.         pine_state->mangled_footer = 1;
  1233.         break;
  1234.         }
  1235.  
  1236.             helper(main_menu_tx, "HELP FOR MAIN MENU", 0);
  1237.         pine_state->mangled_screen = 1;
  1238.         break;
  1239.   
  1240.       /*---------- display other key bindings ------*/
  1241.       case PF2:
  1242.       case OPF2:
  1243.           case 'o' :
  1244.             if(ch == 'o')
  1245.           warn_other_cmds();
  1246.         what = NextTwelve;
  1247.         pine_state->mangled_footer = 1;
  1248.         break;
  1249.  
  1250.       /* case PF4: */
  1251.       /* PF4 is handled above switch */
  1252.  
  1253.           /*---------- Previous item in menu ----------*/
  1254.       case PF5:
  1255.       case 'p':
  1256.       case ctrl('P'):
  1257.       case KEY_UP:
  1258.         if(current_default_menu_item > 1) {
  1259.           current_default_menu_item -= 2;  /* 2 to skip the blank lines */
  1260.           pine_state->mangled_body = 1;
  1261.           if(km->which == 0)
  1262.             pine_state->mangled_footer = 1;
  1263.           just_a_navigate_cmd++;
  1264.         }else {
  1265.           q_status_message(SM_ORDER, 0, 2, "Already at top of list");
  1266.         }
  1267.         break;
  1268.  
  1269.           /*---------- Next item in menu ----------*/
  1270.       case PF6:
  1271.       case 'n':
  1272.       case ctrl('N'):
  1273.       case KEY_DOWN:
  1274.         if(current_default_menu_item < (unsigned)(MAX_DEFAULT_MENU_ITEM-1)){
  1275.           current_default_menu_item += 2;
  1276.           pine_state->mangled_body = 1;
  1277.           if(km->which == 0)
  1278.             pine_state->mangled_footer = 1;
  1279.           just_a_navigate_cmd++;
  1280.         }else {
  1281.           q_status_message(SM_ORDER, 0, 2, "Already at bottom of list");
  1282.         }
  1283.         break;
  1284.  
  1285.           /*---------- Release Notes ----------*/
  1286.       case PF9:
  1287.       case 'r':
  1288.         helper(h_news, "NEWS ABOUT PINE", 0);
  1289.         pine_state->mangled_screen = 1;
  1290.         break;
  1291.  
  1292. #ifndef NO_KEYBOARD_LOCK
  1293.           /*---------- Keyboard lock ----------*/
  1294.       case PF10:
  1295.       case 'k':
  1296.         if(ps_global->restricted || F_ON(F_DISABLE_KBLOCK_CMD,ps_global))
  1297.               goto bleep;
  1298.  
  1299.             (void)lock_keyboard();
  1300.         pine_state->mangled_screen = 1;
  1301.         break;
  1302. #endif /* !NO_KEYBOARD_LOCK */
  1303.  
  1304.           /*---------- Quit pine ----------*/
  1305.       case OPF3: 
  1306.       case 'q':
  1307.       quit_case :
  1308.         pine_state->next_screen = quit_screen;
  1309.         return;
  1310.   
  1311.           /*---------- Go to composer ----------*/
  1312.       case OPF4:
  1313.       case 'c':
  1314.       compose_case :
  1315.         pine_state->next_screen = compose_screen;
  1316.         return;
  1317.   
  1318.           /*---------- Folders ----------*/
  1319.       case OPF5: 
  1320.       case 'l':
  1321.       folder_case :
  1322.         pine_state->next_screen = folder_screen;
  1323.         return;
  1324.  
  1325.           /*---------- Old Folders Command ----------*/
  1326.       case 'f':
  1327.         q_status_message(SM_ORDER, 0, 2, "Use \"L\" to list Folders");
  1328.         break;
  1329.  
  1330.           /*---------- Goto new folder ----------*/
  1331.       case OPF6:
  1332.       case 'g':
  1333.         tc = ps_global->context_current;
  1334.             new_folder = broach_folder(-FOOTER_ROWS(pine_state), 1, &tc);
  1335. #if    defined(DOS) && !defined(_WINDOWS)
  1336.         if(new_folder && *new_folder == '{' && coreleft() < 20000){
  1337.           q_status_message(SM_ORDER | SM_DING, 3, 3,
  1338.                    "Not enough memory to open IMAP folder");
  1339.           new_folder = NULL;
  1340.         }
  1341. #endif
  1342.             if(new_folder)
  1343.           visit_folder(ps_global, new_folder, tc);
  1344.  
  1345.         return;
  1346.  
  1347.           /*---------- Go to index ----------*/
  1348.       case OPF7:
  1349.       case 'i':
  1350.       index_case :
  1351.         pine_state->next_screen = mail_index_screen;
  1352.         return;
  1353.  
  1354.       case OPF8:
  1355.       case 'j':
  1356.         review_messages("REVIEW RECENT MESSAGES");
  1357.         pine_state->mangled_screen = 1;
  1358.         break;
  1359.  
  1360.           /*---------- Setup mini menu ----------*/
  1361.       case OPF9:
  1362.       case 's':
  1363.       setup_case :
  1364.         setup_command = setup_mini_menu(-FOOTER_ROWS(pine_state));
  1365.         pine_state->mangled_footer = 1;
  1366.         do_setup_task(setup_command);
  1367.             if(ps_global->next_screen != main_menu_screen)
  1368.           return;
  1369.         break;
  1370.  
  1371.           /*---------- Go to address book ----------*/
  1372.       case OPF10 :
  1373.       case 'a':
  1374.       addrbook_case :
  1375.         pine_state->next_screen = addr_book_screen;
  1376.         return;
  1377.   
  1378.           /*---------- report a bug ----------*/
  1379.       case OPF11 :
  1380.       case 'b':
  1381.         gripe(ps_global);
  1382.         break;
  1383.  
  1384. #ifdef    DEBUG
  1385.       case '1':
  1386.       case '2':
  1387.       case '3':
  1388.       case '4':
  1389.       case '5':
  1390.       case '6':
  1391.       case '7':
  1392.       case '8':
  1393.       case '9':
  1394.         if(!debug)        /* must have started with debugging */
  1395.           goto bleep;
  1396.  
  1397.         debug = ch - '0';
  1398.         dprint(1, (debugfile, "*** Debug level set to %d ***\n", debug));
  1399.         fflush(debugfile);
  1400.         q_status_message1(SM_ORDER, 0, 1, "Debug level set to %s",
  1401.                   int2string(debug));
  1402.         break;
  1403. #endif    /* DEBUG */
  1404.  
  1405.           case KEY_RESIZE:
  1406.       case ctrl('L'):
  1407.         ClearScreen();
  1408.         pine_state->mangled_screen = 1;
  1409.         break;
  1410.   
  1411. #ifdef    MOUSE
  1412.       case KEY_MOUSE:
  1413.         {   
  1414.         MOUSEPRESS mp;
  1415.         unsigned char ndmi;
  1416.  
  1417.         mouse_get_last (NULL, &mp);
  1418.         ndmi = mp.row - 3;
  1419.         if (mp.row >= 3 && !(ndmi & 0x01)
  1420.             && ndmi <= (unsigned)MAX_DEFAULT_MENU_ITEM
  1421.             && ndmi < pine_state->ttyo->screen_rows
  1422.                        - 4 - FOOTER_ROWS(ps_global)) {
  1423.             if(mp.doubleclick){
  1424.             switch(ndmi){        /* fake main_screen request */
  1425.               case 0 :
  1426.                 goto help_case;
  1427.  
  1428.               case 2 :
  1429.                 goto compose_case;
  1430.  
  1431.               case 4 :
  1432.                 goto index_case;
  1433.  
  1434.               case 6 :
  1435.                 goto folder_case;
  1436.  
  1437.               case 8 :
  1438.                 goto addrbook_case;
  1439.  
  1440.               case 10 :
  1441.                 goto setup_case;
  1442.  
  1443.               case 12 :
  1444.                 goto quit_case;
  1445.  
  1446.               default:            /* no op */
  1447.                 break;
  1448.             }
  1449.             }
  1450.             else{
  1451.             current_default_menu_item = ndmi;
  1452.             pine_state->mangled_body = 1;
  1453.             if(km->which == 0)
  1454.               pine_state->mangled_footer = 1;
  1455.             just_a_navigate_cmd++;
  1456.             }
  1457.         }
  1458.         }
  1459.         break;
  1460. #endif
  1461.  
  1462.       case NO_OP_COMMAND :
  1463.           case NO_OP_IDLE:
  1464.             break;    /* noop for timeout loop mail check */
  1465.   
  1466.       default:
  1467.           bleep:
  1468.         bogus_command(orig_ch, F_ON(F_USE_FK,pine_state) ? "F1" : "?");
  1469.         break;
  1470.      } /* the switch */
  1471.     } /* the BIG while loop! */
  1472. }
  1473.  
  1474.  
  1475.  
  1476. /*----------------------------------------------------------------------
  1477.     Re-Draw the main menu
  1478.  
  1479.     Args: none.
  1480.  
  1481.   Result: main menu is re-displayed
  1482.   ----*/
  1483. void
  1484. main_redrawer()
  1485. {
  1486.     struct key_menu *km = &main_keymenu;
  1487.  
  1488.     ps_global->mangled_screen = 1;
  1489.     show_main_screen(ps_global, 0, FirstMenu, km, 0, (Pos *)NULL);
  1490. }
  1491.  
  1492.  
  1493.     
  1494. /*----------------------------------------------------------------------
  1495.          Draw the main menu
  1496.  
  1497.     Args: pine_state - the usual struct
  1498.       quick_draw - tells do_menu() it can skip some drawing
  1499.       what       - tells which section of keymenu to draw
  1500.       km         - the keymenu
  1501.       cursor_pos - returns a good position for the cursor to be located
  1502.  
  1503.   Result: main menu is displayed
  1504.   ----*/
  1505. void
  1506. show_main_screen(ps, quick_draw, what, km, km_popped, cursor_pos)
  1507.     struct pine     *ps;
  1508.     int             quick_draw;
  1509.     OtherMenu         what;
  1510.     struct key_menu *km;
  1511.     int             km_popped;
  1512.     Pos             *cursor_pos;
  1513. {
  1514.     if(ps->painted_body_on_startup || ps->painted_footer_on_startup){
  1515.     ps->mangled_screen = 0;        /* only worry about it here */
  1516.     ps->mangled_header = 1;        /* we have to redo header */
  1517.     if(!ps->painted_body_on_startup)
  1518.       ps->mangled_body = 1;        /* make sure to paint body*/
  1519.  
  1520.     if(!ps->painted_footer_on_startup)
  1521.       ps->mangled_footer = 1;    /* make sure to paint footer*/
  1522.  
  1523.     ps->painted_body_on_startup   = 0;
  1524.         ps->painted_footer_on_startup = 0;
  1525.     }
  1526.  
  1527.     if(ps->mangled_screen){
  1528.     ps->mangled_header = 1;
  1529.     ps->mangled_body   = 1;
  1530.     ps->mangled_footer = 1;
  1531.     ps->mangled_screen = 0;
  1532.     }
  1533.  
  1534. #ifdef _WINDOWS
  1535.     /* Reset the scroll range.  Main screen never scrolls. */
  1536.     scroll_setrange (0L);
  1537. #endif
  1538.  
  1539.     /* paint the titlebar if needed */
  1540.     if(ps->mangled_header){
  1541.     set_titlebar("MAIN MENU", ps->mail_stream, ps->context_current,
  1542.              ps->cur_folder, ps->msgmap, 1, FolderName, 0, 0);
  1543.     ps->mangled_header = 0;
  1544.     }
  1545.  
  1546.     /* paint the body if needed */
  1547.     if(ps->mangled_body){
  1548.     if(!quick_draw)
  1549.       ClearBody();
  1550.  
  1551.     do_menu(quick_draw, cursor_pos);
  1552.     ps->mangled_body = 0;
  1553.     }
  1554.  
  1555.     /* paint the keymenu if needed */
  1556.     if(ps->mangled_footer){
  1557.     bitmap_t    bitmap;
  1558.     static char label[LONGEST_LABEL + 2 + 1], /* label + brackets + \0 */
  1559.             name[max(LONGEST_NAME,3)+1];  /* longest name+1 (3=F12) */
  1560.  
  1561.     setbitmap(bitmap);
  1562.  
  1563. #ifndef NO_KEYBOARD_LOCK
  1564.     if(ps_global->restricted || F_ON(F_DISABLE_KBLOCK_CMD,ps_global))
  1565. #endif
  1566.       clrbitn(MAIN_KBLOCK_KEY, bitmap);
  1567.  
  1568.     /* put brackets around the default action */
  1569.     label[0] = '[';  label[1] = '\0';
  1570.     strcat(label,
  1571.           km->keys[mkeys[current_default_menu_item].keymenu_number].label);
  1572.     strcat(label, "]");
  1573.     km->keys[MAIN_DEFAULT_KEY].label = label;
  1574.     sprintf(name, "%s", F_ON(F_USE_FK,ps_global) ?
  1575.         pretty_command(mkeys[current_default_menu_item].f_key) :
  1576.         pretty_command(mkeys[current_default_menu_item].key));
  1577.     km->keys[MAIN_DEFAULT_KEY].name = name;
  1578.     if(km_popped){
  1579.         FOOTER_ROWS(ps) = 3;
  1580.         clearfooter(ps);
  1581.     }
  1582.  
  1583.     draw_keymenu(km, bitmap, ps_global->ttyo->screen_cols,
  1584.         1-FOOTER_ROWS(ps_global), 0, what, 0);
  1585.     ps->mangled_footer = 0;
  1586.     if(km_popped){
  1587.         FOOTER_ROWS(ps) = 1;
  1588.         mark_keymenu_dirty();
  1589.     }
  1590.     }
  1591. }
  1592.  
  1593.  
  1594. /*----------------------------------------------------------------------
  1595.          Actually display the main menu
  1596.  
  1597.     Args: quick_draw - just a next or prev command was typed so we only have
  1598.                to redraw the highlighting
  1599.           cursor_pos - a place to return a good value for cursor location
  1600.  
  1601.   Result: Main menu is displayed
  1602.   ---*/
  1603. void
  1604. do_menu(quick_draw, cursor_pos)
  1605.     int  quick_draw;
  1606.     Pos *cursor_pos;
  1607. {
  1608.     int  dline, indent, longest = 0, i;
  1609.     char buf[MAX_DEFAULT_MENU_ITEM+1][MAX_SCREEN_COLS+1], *p;
  1610.     static int last_inverse = -1;
  1611.     Pos pos;
  1612.  
  1613.  
  1614.     /*
  1615.      * Build all the menu lines...
  1616.      */
  1617.     for(dline = 0;
  1618.     mkeys[dline].key_and_name && dline < ps_global->ttyo->screen_rows - 7;
  1619.     dline++){
  1620.     memset((void *)buf[dline], ' ', MAX_SCREEN_COLS * sizeof(char));
  1621.         sprintf(buf[dline], mkeys[dline].key_and_name,
  1622.                 F_ON(F_USE_FK,ps_global)
  1623.           ? "" : pretty_command(mkeys[dline].key),
  1624.         (ps_global->VAR_NEWS_SPEC && mkeys[dline].news_addition)
  1625.           ? mkeys[dline].news_addition : "");
  1626.  
  1627.     if(longest < (indent = strlen(buf[dline])))
  1628.       longest = indent;
  1629.  
  1630.     buf[dline][indent] = ' ';    /* buf's really tied off below */
  1631.     }
  1632.  
  1633.     indent = max(((ps_global->ttyo->screen_cols - longest)/2) - 1, 0);
  1634.  
  1635.     /* leave room for keymenu, status line, and trademark message */
  1636.     for(dline = 3;
  1637.     mkeys[dline-3].key_and_name
  1638.         && dline < ps_global->ttyo->screen_rows-(FOOTER_ROWS(ps_global)+1);
  1639.     dline++){
  1640.     if(quick_draw && !(dline-3 == last_inverse
  1641.                || dline-3 == current_default_menu_item))
  1642.       continue;
  1643.  
  1644.     if(dline-3 == current_default_menu_item)
  1645.       StartInverse();
  1646.  
  1647.     buf[dline-3][min(ps_global->ttyo->screen_cols-indent,longest+1)]= '\0';
  1648.     pos.row = dline;
  1649.     pos.col = indent;
  1650.         PutLine0(pos.row, pos.col, buf[dline-3]);
  1651.  
  1652.     if(dline-3 == current_default_menu_item){
  1653.         if(cursor_pos){
  1654.         cursor_pos->row = pos.row;
  1655.         cursor_pos->col = pos.col + 6;
  1656.         if(F_OFF(F_USE_FK,ps_global))
  1657.           cursor_pos->col++;
  1658.         }
  1659.  
  1660.         EndInverse();
  1661.     }
  1662.     }
  1663.  
  1664.     last_inverse = current_default_menu_item;
  1665.  
  1666.     if(!quick_draw)    /* the devi.. uh, I mean, lawyer made me do it. */
  1667.       PutLine0(ps_global->ttyo->screen_rows - (FOOTER_ROWS(ps_global)+1),
  1668.       3, LEGAL_NOTICE);
  1669.  
  1670.     fflush(stdout);
  1671. }
  1672.  
  1673.  
  1674. /*----------------------------------------------------------------------
  1675.  
  1676. Args: ql -- Line to prompt on
  1677. Returns: character selected
  1678.  
  1679.   ----*/
  1680. int
  1681. setup_mini_menu(ql)
  1682.      int ql;
  1683. {
  1684.     char        prompt[80];
  1685.     char        letters[20];
  1686.     char        *printer  = "Printer";
  1687.     char        *passwd   = "Newpassword";
  1688.     char        *config   = "Config";
  1689.     char        *update   = "Update";
  1690.     char    *sigedit  = "Signature";
  1691.     HelpType     help     = h_mini_setup;
  1692.     int          deefault = 'p';
  1693.     int          s, ekey_num;
  1694.     ESCKEY_S     setup_names[6];
  1695.  
  1696.     ekey_num = 0;
  1697.  
  1698.     /* There isn't anything that is allowed */
  1699.     if(ps_global->vars[V_PRINTER].is_fixed &&
  1700.        F_ON(F_DISABLE_PASSWORD_CMD,ps_global) &&
  1701.        F_ON(F_DISABLE_CONFIG_SCREEN,ps_global) &&
  1702.        F_ON(F_DISABLE_UPDATE_CMD,ps_global) && 
  1703.        F_ON(F_DISABLE_SIGEDIT_CMD,ps_global)){
  1704.       q_status_message(SM_ORDER | SM_DING, 3, 4,
  1705.              "All setup tasks turned off or prohibited by Sys. Mgmt.");
  1706.       return('e');
  1707.     }
  1708.  
  1709.     if(!ps_global->vars[V_PRINTER].is_fixed){ /* printer can be changed */
  1710.     setup_names[ekey_num].ch      = 'p';
  1711.     setup_names[ekey_num].rval    = 'p';
  1712.     setup_names[ekey_num].name    = "P";
  1713.     setup_names[ekey_num++].label = printer;
  1714.     }
  1715.  
  1716.     if(F_OFF(F_DISABLE_PASSWORD_CMD,ps_global)){ /* password is allowed */
  1717.     setup_names[ekey_num].ch      = 'n';
  1718.     setup_names[ekey_num].rval    = 'n';
  1719.     setup_names[ekey_num].name    = "N";
  1720.     setup_names[ekey_num++].label = passwd;
  1721.     }
  1722.  
  1723.     if(F_OFF(F_DISABLE_CONFIG_SCREEN,ps_global)){ /* config allowed */
  1724.     setup_names[ekey_num].ch      = 'c';
  1725.     setup_names[ekey_num].rval    = 'c';
  1726.     setup_names[ekey_num].name    = "C";
  1727.     setup_names[ekey_num++].label = config;
  1728.     }
  1729.  
  1730.     if(F_OFF(F_DISABLE_UPDATE_CMD,ps_global)){ /* update is allowed */
  1731.     setup_names[ekey_num].ch      = 'u';
  1732.     setup_names[ekey_num].rval    = 'u';
  1733.     setup_names[ekey_num].name    = "U";
  1734.     setup_names[ekey_num++].label = update;
  1735.     }
  1736.  
  1737.     if(F_OFF(F_DISABLE_SIGEDIT_CMD,ps_global)){ /* .sig editing is allowed */
  1738.     setup_names[ekey_num].ch      = 's';
  1739.     setup_names[ekey_num].rval    = 's';
  1740.     setup_names[ekey_num].name    = "S";
  1741.     setup_names[ekey_num++].label = sigedit;
  1742.     }
  1743.  
  1744.     setup_names[ekey_num].ch    = -1;
  1745.  
  1746.     if(F_ON(F_BLANK_KEYMENU,ps_global)){
  1747.     char *p;
  1748.  
  1749.     p = letters;
  1750.     *p = '\0';
  1751.     for(ekey_num = 0; setup_names[ekey_num].ch != -1; ekey_num++){
  1752.         *p++ = setup_names[ekey_num].ch;
  1753.         if(setup_names[ekey_num + 1].ch != -1)
  1754.           *p++ = ',';
  1755.     }
  1756.  
  1757.     *p = '\0';
  1758.     }
  1759.  
  1760.     sprintf(prompt,
  1761.         "Choose a setup task from %s : ",
  1762.         F_ON(F_BLANK_KEYMENU,ps_global) ? letters : "the menu below");
  1763.  
  1764.     s = radio_buttons(prompt, ql, setup_names, deefault, 'x', help, RB_NORM);
  1765.     /* ^C */
  1766.     if(s == 'x') {
  1767.     q_status_message(SM_ORDER,0,3,"Setup command cancelled");
  1768.     s = 'e';
  1769.     }
  1770.  
  1771.     return(s);
  1772. }
  1773.  
  1774.  
  1775. /*----------------------------------------------------------------------
  1776.  
  1777. Args: command -- command char to perform
  1778.  
  1779.   ----*/
  1780. void
  1781. do_setup_task(command)
  1782.     int command;
  1783. {
  1784.     switch(command) {
  1785.         /*----- UPDATE -----*/
  1786.       case 'u':
  1787.     {
  1788.         char update_folder[256];
  1789.  
  1790.         q_status_message(SM_ORDER, 3, 5, "Connecting to update server");
  1791.         sprintf(update_folder, "%supdates.pine%s", UPDATE_FOLDER,
  1792.             PHONE_HOME_VERSION);
  1793.         visit_folder(ps_global, update_folder, NULL);
  1794.         ps_global->mangled_screen = 1;
  1795.     }
  1796.  
  1797.     break;
  1798.  
  1799.         /*----- EDIT SIGNATURE -----*/
  1800.       case 's':
  1801.     signature_edit(ps_global->VAR_SIGNATURE_FILE);
  1802.     ps_global->mangled_screen = 1;
  1803.     break;
  1804.  
  1805.         /*----- CONFIGURE OPTIONS -----*/
  1806.       case 'c':
  1807.     option_screen(ps_global);
  1808.     ps_global->mangled_screen = 1;
  1809.     break;
  1810.  
  1811.         /*----- EXIT -----*/
  1812.       case 'e':
  1813.         break;
  1814.  
  1815.         /*----- NEW PASSWORD -----*/
  1816.       case 'n':
  1817. #ifdef    PASSWD_PROG
  1818.         if(ps_global->restricted){
  1819.         q_status_message(SM_ORDER, 3, 5,
  1820.         "Password change unavailable in restricted demo version of Pine.");
  1821.         }else {
  1822.         change_passwd();
  1823.         ClearScreen();
  1824.         ps_global->mangled_screen = 1;
  1825.     }
  1826. #else
  1827.         q_status_message(SM_ORDER, 0, 5,
  1828.          "Password changing not configured for this version of Pine.");
  1829.     display_message('x');
  1830. #endif    /* DOS */
  1831.         break;
  1832.  
  1833.         /*----- CHOOSE PRINTER ------*/
  1834.       case 'p':
  1835. #ifdef    DOS
  1836.     q_status_message(SM_ORDER, 3, 5,
  1837.         "Printer configuration not available in the DOS version of Pine.");
  1838. #else
  1839.         select_printer(ps_global); 
  1840.     ps_global->mangled_screen = 1;
  1841. #endif
  1842.         break;
  1843.     }
  1844. }
  1845.  
  1846.  
  1847. /*
  1848.  * Make sure any errors during initialization get queued for display
  1849.  */
  1850. void
  1851. queue_init_errors(ps)
  1852.     struct pine *ps;
  1853. {
  1854.     int i;
  1855.  
  1856.     if(ps->init_errs){
  1857.     for(i = 0; ps->init_errs[i]; i++){
  1858.         q_status_message(SM_ORDER | SM_DING, 3, 5, ps->init_errs[i]);
  1859.         fs_give((void **)&ps->init_errs[i]);
  1860.     }
  1861.  
  1862.     fs_give((void **)&ps->init_errs);
  1863.     }
  1864. }
  1865.  
  1866.  
  1867. /*
  1868.  * Display a new user or new version message.
  1869.  */
  1870. void
  1871. new_user_or_version(message)
  1872.     char message[];
  1873. {
  1874.     char buf[256];
  1875.     int dline, i, j;
  1876.  
  1877.     j = 0;
  1878.     for(dline = 2;
  1879.     dline < ps_global->ttyo->screen_rows - (FOOTER_ROWS(ps_global)+1);
  1880.     dline++){
  1881.     for(i = 0; i < 256 && message[j] && message[j] != '\n' ; i++)
  1882.       buf[i] = message[j++];
  1883.  
  1884.     buf[i] = '\0';
  1885.     if(message[j])
  1886.       j++;
  1887.     else if(!i)
  1888.       break;
  1889.  
  1890.         PutLine0(dline, 2, buf);
  1891.     }
  1892.  
  1893.     /*
  1894.      * Temporary message for old bug.
  1895.      * 3.89 and earlier would delete continuation lines of variables
  1896.      * it didn't know about, so new *list* variables added would be
  1897.      * truncated to length 1 if a user went back to a previous version.
  1898.      * There are many new list vars in 3.90 so it could be a problem now.
  1899.      * Check to see if the new list variables are set and may have been
  1900.      * truncated, and issue a warning.  This works because 3.89 and earlier
  1901.      * rewrite the version number in pinerc even if it goes backwards.
  1902.      */
  1903.     if(ps_global->pre390)
  1904.       truncated_listvars_warning(&dline);
  1905.  
  1906.     PutLine0(ps_global->ttyo->screen_rows - (FOOTER_ROWS(ps_global)+1), 13,
  1907.          "PINE is a trademark of the University of Washington.");
  1908.  
  1909.     /*
  1910.      * You may think this is weird.  We're trying to offer sending
  1911.      * the "phone home" message iff there's no evidence we've ever
  1912.      * run pine *or* this is the first time we've run a version
  1913.      * a version of pine >= 3.90.  The check for the existence of a var
  1914.      * new in 3.90, is to compensate for pre 3.90 pine's rewriting their
  1915.      * lower version number.
  1916.      */
  1917.     if(ps_global->first_time_user
  1918.        || (ps_global->pre390
  1919.        && !var_in_pinerc(ps_global->vars[V_NNTP_SERVER].name))
  1920.        || !var_in_pinerc(ps_global->vars[V_MAILCHECK].name)){
  1921.     phone_home_blurb(dline);
  1922.     if(want_to("Request document", 'y', 0, NO_HELP, 0, 1) == 'y')
  1923.       phone_home();
  1924.     else
  1925.       q_status_message(SM_ORDER,0,3,"No request sent");
  1926.  
  1927.     }
  1928.     else{
  1929.     StartInverse();
  1930.     PutLine0(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global), 0,
  1931.          "Type any character to continue : ");
  1932.     EndInverse();
  1933.  
  1934.     /* ignore the character typed, 120 second timeout */
  1935.     (void)read_char(120);
  1936.     }
  1937.  
  1938.     /*
  1939.      * Check to see if we have an old style postponed mail folder and
  1940.      * that there isn't a new style postponed folder.  If true,
  1941.      * fix up the old one, and move it into postition for pine >= 3.90...
  1942.      */
  1943.     if(!ps_global->first_time_user && ps_global->pre390)
  1944.       upgrade_old_postponed();
  1945. }
  1946.  
  1947.  
  1948. void
  1949. upgrade_old_postponed()
  1950. {
  1951.     int           i;
  1952.     char       file_path[MAXPATH], *status, buf[6];
  1953.     STORE_S   *in_so, *out_so;
  1954.     CONTEXT_S *save_cntxt = default_save_context(ps_global->context_list);
  1955.     STRING     msgtxt;
  1956.     gf_io_t    pc, gc;
  1957.  
  1958.     /*
  1959.      * NOTE: woe to he who redefines things in os.h such that
  1960.      * the new and old pastponed folder names are the same.
  1961.      * If so, you're on your own...
  1962.      */
  1963.     build_path(file_path, ps_global->folders_dir, POSTPONED_MAIL);
  1964.     if(in_so = so_get(FileStar, file_path, READ_ACCESS)){
  1965.     for(i = 0; i < 6 && so_readc((unsigned char *)&buf[i], in_so); i++)
  1966.       ;
  1967.  
  1968.     buf[i] = '\0';
  1969.     if(strncmp(buf, "From ", 5)){
  1970.         dprint(1, (debugfile,
  1971.                "POSTPONED conversion %s --> <%s>%s\n",
  1972.                file_path,save_cntxt->context,
  1973.                ps_global->VAR_POSTPONED_FOLDER));
  1974.         so_seek(in_so, 0L, 0);
  1975.         if((out_so = so_get(CharStar, NULL, WRITE_ACCESS))
  1976.            && (folder_exists(save_cntxt->context,
  1977.                  ps_global->VAR_POSTPONED_FOLDER) > 0
  1978.            || context_create(save_cntxt->context, NULL,
  1979.                      ps_global->VAR_POSTPONED_FOLDER))){
  1980.         gf_set_so_readc(&gc, in_so);
  1981.         gf_set_so_writec(&pc, out_so);
  1982.         gf_filter_init();
  1983.         gf_link_filter(gf_local_nvtnl);
  1984.         if(!(status = gf_pipe(gc, pc))){
  1985.             so_seek(out_so, 0L, 0);    /* just in case */
  1986.             INIT(&msgtxt, mail_string, so_text(out_so),
  1987.              strlen((char *)so_text(out_so)));
  1988.  
  1989.             if(context_append(save_cntxt->context, NULL, 
  1990.                       ps_global->VAR_POSTPONED_FOLDER,
  1991.                       &msgtxt)){
  1992.             so_give(&in_so);
  1993.             unlink(file_path);
  1994.             }
  1995.             else{
  1996.             q_status_message(SM_ORDER | SM_DING, 3, 5,
  1997.                     "Problem upgrading postponed message");
  1998.             dprint(1, (debugfile,
  1999.                    "Conversion failed: Can't APPEND\n"));
  2000.             }
  2001.         }
  2002.         else{
  2003.             q_status_message(SM_ORDER | SM_DING, 3, 5,
  2004.                      "Problem upgrading postponed message");
  2005.             dprint(1,(debugfile,"Conversion failed: %s\n",status));
  2006.         }
  2007.         }
  2008.         else{
  2009.         q_status_message(SM_ORDER | SM_DING, 3, 5,
  2010.                  "Problem upgrading postponed message");
  2011.         dprint(1, (debugfile,
  2012.                "Conversion failed: Can't create %s\n",
  2013.                (out_so) ? "new postponed folder"
  2014.                     : "temp storage object"));
  2015.         }
  2016.  
  2017.         if(out_so)
  2018.           so_give(&out_so);
  2019.     }
  2020.  
  2021.     if(in_so)
  2022.       so_give(&in_so);
  2023.     }
  2024. }
  2025.  
  2026.  
  2027. void
  2028. phone_home_blurb(dline)
  2029.     int dline;
  2030. {
  2031.     static char *blurb[] = {
  2032.       "SPECIAL OFFER:  Would you like to receive (via email) a brief document",
  2033.       "  entitled \"Getting the most out of Pine\" ?"
  2034.     };
  2035.  
  2036.     if(dline+3 < ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global))
  2037.       dline++;
  2038.  
  2039.     if(dline+2 < ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)){
  2040.     PutLine0(dline++, 2, blurb[0]);
  2041.     PutLine0(dline++, 2, blurb[1]);
  2042.     }
  2043. }
  2044.  
  2045.  
  2046. void
  2047. truncated_listvars_warning(dline)
  2048.     int *dline;
  2049. {
  2050.     char **p;
  2051.     int    i;
  2052. #define N_LISTS 9
  2053.     char **new_lists[N_LISTS];
  2054.  
  2055.     i = 0;
  2056.     new_lists[i++] = ps_global->USR_COMP_HDRS;
  2057.     new_lists[i++] = ps_global->USR_CUSTOM_HDRS;
  2058.     new_lists[i++] = ps_global->USR_NNTP_SERVER;
  2059.     new_lists[i++] = ps_global->USR_ADDRESSBOOK;
  2060.     new_lists[i++] = ps_global->USR_GLOB_ADDRBOOK;
  2061.     new_lists[i++] = ps_global->USR_FORCED_ABOOK_ENTRY;
  2062.     new_lists[i++] = ps_global->USR_DISPLAY_FILTERS;
  2063.     new_lists[i++] = ps_global->USR_ALT_ADDRS;
  2064.     new_lists[i++] = ps_global->USR_ABOOK_FORMATS;
  2065.  
  2066.     for(i=0; i < N_LISTS; i++){
  2067.         p = new_lists[i];
  2068.         if(p && *p && **p)
  2069.       break;
  2070.     }
  2071.  
  2072. #define LINES_OF_EXPLANATION 2
  2073.     if(i < N_LISTS){
  2074.       if((*dline)+LINES_OF_EXPLANATION+1
  2075.          < ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global))
  2076.       (*dline)++;
  2077.       if((*dline)+LINES_OF_EXPLANATION
  2078.          < ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)){
  2079.     PutLine0((*dline)++, 2,
  2080.     "[You ran an old version of Pine which may have truncated some of your]");
  2081.     PutLine0((*dline)++, 2,
  2082.     "[new pinerc list variables (because of a bug in the old version).    ]");
  2083.       }
  2084.     }
  2085. }
  2086.  
  2087.  
  2088. /*----------------------------------------------------------------------
  2089.           Quit pine if the user wants to 
  2090.  
  2091.     Args: The usual pine structure
  2092.  
  2093.   Result: User is asked if she wants to quit, if yes then execute quit.
  2094.  
  2095.        Q U I T    S C R E E N
  2096.  
  2097. Not really a full screen. Just count up deletions and ask if we really
  2098. want to quit.
  2099.   ----*/
  2100. void
  2101. quit_screen(pine_state)
  2102.     struct pine *pine_state;
  2103. {
  2104.     dprint(1, (debugfile, "\n\n    ---- QUIT SCREEN ----\n"));    
  2105.  
  2106.     if(!pine_state->nr_mode && F_OFF(F_QUIT_WO_CONFIRM,pine_state)
  2107.        && want_to("Really quit pine", 'y', 0, NO_HELP, 0, 0) != 'y') {
  2108.         pine_state->next_screen = pine_state->prev_screen;
  2109.         return;
  2110.     }
  2111.  
  2112.     goodnight_gracey(pine_state, 0);
  2113. }
  2114.  
  2115.  
  2116.  
  2117. /*----------------------------------------------------------------------
  2118.     The nuts and bolts of actually cleaning up and exitting pine
  2119.  
  2120.     Args: ps -- the usual pine structure, 
  2121.       exit_val -- what to tell our parent
  2122.  
  2123.   Result: This never returns
  2124.  
  2125.   ----*/
  2126. void
  2127. goodnight_gracey(pine_state, exit_val)
  2128.     struct pine *pine_state;
  2129.     int         exit_val;
  2130. {
  2131.     int   i, cur_is_inbox;
  2132.     char *final_msg = NULL, *p;
  2133.     char  msg[MAX_SCREEN_COLS+1];
  2134.     char *pf = "Pine finished";
  2135.  
  2136.     cur_is_inbox = (pine_state->inbox_stream == pine_state->mail_stream);
  2137.  
  2138.     /* clean up open streams */
  2139.     if(pine_state->mail_stream)
  2140.       expunge_and_close(pine_state->mail_stream, pine_state->context_current,
  2141.             pine_state->cur_folder,
  2142.             (!pine_state->inbox_stream || cur_is_inbox)
  2143.               ? &final_msg : NULL);
  2144.     if(pine_state->msgmap)
  2145.       mn_give(&pine_state->msgmap);
  2146.  
  2147.     pine_state->redrawer = (void (*)())NULL;
  2148.  
  2149.     if(pine_state->inbox_stream && !cur_is_inbox){
  2150.     pine_state->mail_stream = pine_state->inbox_stream;
  2151.     pine_state->msgmap      = pine_state->inbox_msgmap;
  2152.         expunge_and_close(pine_state->inbox_stream, NULL,
  2153.               pine_state->inbox_name, &final_msg);
  2154.     mn_give(&pine_state->msgmap);
  2155.     }
  2156.  
  2157.     if(pine_state->outstanding_pinerc_changes)
  2158.       write_pinerc(pine_state);
  2159.  
  2160.     if(final_msg){
  2161.     strcpy(msg, pf);
  2162.     strcat(msg, " -- ");
  2163.     strncat(msg, final_msg, MAX_SCREEN_COLS - strlen(msg));
  2164.     fs_give((void **)&final_msg);
  2165.     }
  2166.     else
  2167.       strcpy(msg, pf);
  2168.  
  2169.     end_screen(msg);
  2170.     end_titlebar();
  2171.     end_keymenu();
  2172.  
  2173.     end_keyboard(F_ON(F_USE_FK,pine_state));
  2174.     end_tty_driver(pine_state);
  2175. #if !defined(DOS) && !defined(OS2)
  2176.     kbdestroy(pine_state->kbesc);
  2177. #endif
  2178.     end_signals(0);
  2179.     if(filter_data_file(0))
  2180.       unlink(filter_data_file(0));
  2181.  
  2182.     imap_flush_passwd_cache();
  2183.     clear_index_cache();
  2184.     completely_done_with_adrbks();
  2185.     free_newsgrp_cache();
  2186.     mailcap_free();
  2187.     free_folders();
  2188.  
  2189.     if(p = (char *) mail_parameters(NULL, GET_USERNAMEBUF, NULL)){
  2190.     fs_give((void **)&p);
  2191.     (void) mail_parameters(NULL, SET_USERNAMEBUF, NULL);
  2192.     }
  2193.  
  2194.     if(pine_state->hostname != NULL)
  2195.       fs_give((void **)&pine_state->hostname);
  2196.     if(pine_state->localdomain != NULL)
  2197.       fs_give((void **)&pine_state->localdomain);
  2198.     if(pine_state->ttyo != NULL)
  2199.       fs_give((void **)&pine_state->ttyo);
  2200.     if(pine_state->home_dir != NULL)
  2201.       fs_give((void **)&pine_state->home_dir);
  2202.     if(pine_state->folders_dir != NULL)
  2203.       fs_give((void **)&pine_state->folders_dir);
  2204.     if(pine_state->ui.homedir)
  2205.       fs_give((void **)&pine_state->ui.homedir);
  2206.     if(pine_state->ui.login)
  2207.       fs_give((void **)&pine_state->ui.login);
  2208.     if(pine_state->ui.fullname)
  2209.       fs_give((void **)&pine_state->ui.fullname);
  2210.     if(pine_state->index_disp_format)
  2211.       fs_give((void **)&pine_state->index_disp_format);
  2212.     if(pine_state->pinerc)
  2213.       fs_give((void **)&pine_state->pinerc);
  2214. #if defined(DOS) || defined(OS2)
  2215.     if(pine_state->pine_dir)
  2216.       fs_give((void **)&pine_state->pine_dir);
  2217. #endif
  2218.  
  2219.     if(ps_global->atmts){
  2220.     for(i = 0; ps_global->atmts[i].description; i++){
  2221.         fs_give((void **)&ps_global->atmts[i].description);
  2222.         fs_give((void **)&ps_global->atmts[i].number);
  2223.     }
  2224.  
  2225.     fs_give((void **)&ps_global->atmts);
  2226.     }
  2227.  
  2228.     free_vars(pine_state);
  2229.     free_pinerc_lines();
  2230.  
  2231.     fs_give((void **)&pine_state);
  2232.  
  2233. #ifdef DEBUG
  2234.     if(debugfile)
  2235.       fclose(debugfile);
  2236. #endif    
  2237.  
  2238.     exit(exit_val);
  2239. }
  2240.  
  2241.  
  2242. /*
  2243.  * Useful flag checking macro for 
  2244.  */
  2245. #define FLAG_MATCH(F, M)   (((((F)&F_SEEN)   ? (M)->seen             \
  2246.                 : ((F)&F_UNSEEN)     ? !(M)->seen : 1)         \
  2247.               && (((F)&F_DEL)    ? (M)->deleted             \
  2248.                 : ((F)&F_UNDEL)      ? !(M)->deleted : 1)    \
  2249.               && (((F)&F_ANS)    ? (M)->answered             \
  2250.                 : ((F)&F_UNANS)         ? !(M)->answered : 1)   \
  2251.               && (((F)&F_FLAG) ? (M)->flagged             \
  2252.                 : ((F)&F_UNFLAG)   ? !(M)->flagged : 1)         \
  2253.               && (((F)&F_RECENT) ? (M)->recent             \
  2254.                 : ((F)&F_UNRECENT)   ? !(M)->recent : 1))    \
  2255.               || ((((F)&F_OR_SEEN) ? (M)->seen             \
  2256.                 : ((F)&F_OR_UNSEEN)   ? !(M)->seen : 0)      \
  2257.               || (((F)&F_OR_DEL)   ? (M)->deleted             \
  2258.                 : ((F)&F_OR_UNDEL)    ? !(M)->deleted : 0)   \
  2259.               || (((F)&F_OR_ANS)   ? (M)->answered             \
  2260.                 : ((F)&F_OR_ANS)      ? !(M)->answered : 0)  \
  2261.               || (((F)&F_OR_FLAG)? (M)->flagged             \
  2262.                 : ((F)&F_OR_UNFLAG) ? !(M)->flagged : 0)     \
  2263.               || (((F)&F_OR_RECENT)? (M)->recent             \
  2264.                 : ((F)&F_OR_UNRECENT) ? !(M)->recent : 0)))
  2265.  
  2266.  
  2267.  
  2268. /*----------------------------------------------------------------------
  2269.      Find the first message with the specified flags set
  2270.  
  2271.   Args: flags -- Flags in messagecache to match on
  2272.         stream -- The stream/folder to look at message status
  2273.  
  2274.  Result: Message number of first message with specified flags set
  2275.   ----------------------------------------------------------------------*/
  2276. MsgNo
  2277. first_sorted_flagged(flags, stream)
  2278.     unsigned long  flags;
  2279.     MAILSTREAM    *stream;
  2280. {
  2281.     MsgNo        i;
  2282.     MESSAGECACHE *mc;
  2283.  
  2284.     FETCH_ALL_FLAGS(stream);
  2285.  
  2286.     for(i = 1 ; i < mn_get_total(ps_global->msgmap); i++) {
  2287.     mc = mail_elt(stream, mn_m2raw(ps_global->msgmap, i));
  2288.     if(mc && FLAG_MATCH(flags, mc))
  2289.           break;
  2290.     }
  2291.  
  2292.     dprint(4, (debugfile, "First unseen returning %ld\n", (long)i));
  2293.     return(i);
  2294. }
  2295.  
  2296.  
  2297.  
  2298. /*----------------------------------------------------------------------
  2299.      Find the next message with specified flags set
  2300.  
  2301.   Args: flags -- Flags in messagecache to match on
  2302.         stream -- The stream/folder to look at message status
  2303.         start  -- Place to start looking
  2304.         found_is_new -- Set if the message found is actually new.  The only
  2305.                         case where this might not be set when the message
  2306.                         being returned is the last message in folder.
  2307.  
  2308.  Result: Message number of next message with specified flags set
  2309.   ----------------------------------------------------------------------*/
  2310. MsgNo
  2311. next_sorted_flagged(flags, stream, start, found_is_new)
  2312.     unsigned long  flags;
  2313.     MAILSTREAM    *stream;
  2314.     long           start;
  2315.     int           *found_is_new;
  2316. {
  2317.     MsgNo        i;
  2318.     MESSAGECACHE *mc;
  2319.  
  2320.     if(found_is_new)
  2321.       *found_is_new = 0;
  2322.  
  2323.     FETCH_ALL_FLAGS(stream);
  2324.  
  2325.     for(i = start ; i <= mn_get_total(ps_global->msgmap); i++) {
  2326.     mc = mail_elt(stream, mn_m2raw(ps_global->msgmap, i));
  2327.         if(mc && FLAG_MATCH(flags, mc)
  2328.        && !get_lflag(stream, ps_global->msgmap, i, MN_HIDE)){
  2329.             if(found_is_new)
  2330.               *found_is_new = 1;
  2331.  
  2332.             break;
  2333.         }
  2334.     }
  2335.  
  2336.     return(min(i, mn_get_total(ps_global->msgmap)));
  2337. }
  2338.  
  2339.  
  2340.  
  2341. /*----------------------------------------------------------------------
  2342.   get the requested LOCAL flag bits for the given pine message number
  2343.  
  2344.    Accepts: msgs - pointer to message manipulation struct
  2345.             n - message number to get
  2346.         f - bitmap of interesting flags
  2347.    Returns: non-zero if flag set, 0 if not set or no elt (error?)
  2348.  
  2349.    NOTE: this can be used to test system flags
  2350.   ----*/
  2351. int
  2352. get_lflag(stream, msgs, n, f)
  2353.      MAILSTREAM *stream;
  2354.      MSGNO_S    *msgs;
  2355.      long        n;
  2356.      int         f;
  2357. {
  2358.     MESSAGECACHE *mc;
  2359.  
  2360.     if(n < 1L || (msgs && n > mn_get_total(msgs)))
  2361.       return(0);
  2362.  
  2363.     FETCH_ALL_FLAGS(stream);
  2364.  
  2365.     mc = mail_elt(stream, msgs ? mn_m2raw(msgs, n) : n);
  2366.     return((!mc) ? 0 : (!f) ? !(mc->spare || mc->spare2 || mc->spare3)
  2367.                 : (((f & MN_HIDE) ? mc->spare : 0)
  2368.                    || ((f & MN_EXLD) ? mc->spare2 : 0)
  2369.                    || ((f & MN_SLCT) ? mc->spare3 : 0)));
  2370. }
  2371.  
  2372.  
  2373.  
  2374. /*----------------------------------------------------------------------
  2375.   set the requested LOCAL flag bits for the given pine message number
  2376.  
  2377.    Accepts: msgs - pointer to message manipulation struct
  2378.             n - message number to set
  2379.         f - bitmap of interesting flags
  2380.         v - value (on or off) flag should get
  2381.    Returns: our index number of first
  2382.  
  2383.    NOTE: this isn't to be used for setting IMAP system flags
  2384.   ----*/
  2385. int
  2386. set_lflag(stream, msgs, n, f, v)
  2387.      MAILSTREAM *stream;
  2388.      MSGNO_S    *msgs;
  2389.      long        n;
  2390.      int         f, v;
  2391. {
  2392.     MESSAGECACHE *mc;
  2393.  
  2394.     if(n < 1L || n > mn_get_total(msgs))
  2395.       return(0L);
  2396.  
  2397.     FETCH_ALL_FLAGS(stream);
  2398.  
  2399.     if(mc = mail_elt(stream, mn_m2raw(msgs, n))){
  2400.     if((f & MN_HIDE) && mc->spare != v){
  2401.         mc->spare = v;
  2402.         msgs->flagged_hid += (v) ? 1L : -1L;
  2403.     }
  2404.  
  2405.     if((f & MN_EXLD) && mc->spare2 != v){
  2406.         mc->spare2 = v;
  2407.         msgs->flagged_exld += (v) ? 1L : -1L;
  2408.     }
  2409.  
  2410.     if((f & MN_SLCT) && mc->spare3 != v){
  2411.         mc->spare3 = v;
  2412.         msgs->flagged_tmp += (v) ? 1L : -1L;
  2413.     }
  2414.     }
  2415.  
  2416.     return(1);
  2417. }
  2418.  
  2419.  
  2420.  
  2421. /*----------------------------------------------------------------------
  2422.   return whether the given flag is set somewhere in the folder
  2423.  
  2424.    Accepts: msgs - pointer to message manipulation struct
  2425.         f - flag bitmap to act on
  2426.    Returns: number of messages with the given flag set.
  2427.         NOTE: the sum, if multiple flags tested, is bogus
  2428.   ----*/
  2429. long
  2430. any_lflagged(msgs, f)
  2431.      MSGNO_S    *msgs;
  2432.      int         f;
  2433. {
  2434.     if(!msgs)
  2435.       return(0L);
  2436.  
  2437.     if(f == MN_NONE)
  2438.       return(!(msgs->flagged_hid || msgs->flagged_exld || msgs->flagged_tmp));
  2439.     else
  2440.       return(((f & MN_HIDE)   ? msgs->flagged_hid  : 0L)
  2441.          + ((f & MN_EXLD) ? msgs->flagged_exld : 0L)
  2442.          + ((f & MN_SLCT) ? msgs->flagged_tmp  : 0L));
  2443. }
  2444.  
  2445.  
  2446.  
  2447. /*----------------------------------------------------------------------
  2448.   Decrement the count of the given flag type messages
  2449.  
  2450.    Accepts: msgs - pointer to message manipulation struct
  2451.         f - flag bitmap to act on
  2452.         n - number of flags
  2453.    Returns: with the total count adjusted accordingly.
  2454.         NOTE: this function is kind of bogus, and is mostly 
  2455.         necessary because flags are stored in elt's which we can't
  2456.         get at during an mm_expunged callback.
  2457.   ----*/
  2458. void
  2459. dec_lflagged(msgs, f, n)
  2460.      MSGNO_S    *msgs;
  2461.      int         f;
  2462.      long     n;
  2463. {
  2464.     if(!msgs)
  2465.       return;
  2466.  
  2467.     if(f & MN_HIDE)
  2468.       msgs->flagged_hid = max(0L, msgs->flagged_hid - n);
  2469.     else if(f & MN_EXLD)
  2470.       msgs->flagged_exld = max(0L, msgs->flagged_exld - n);
  2471.     else if(f & MN_SLCT)
  2472.       msgs->flagged_tmp = max(0L, msgs->flagged_tmp - n);
  2473. }
  2474.  
  2475.  
  2476.  
  2477. /*----------------------------------------------------------------------
  2478.     Count messages on stream with specified system flag attributes
  2479.  
  2480.   Args: stream -- The stream/folder to look at message status
  2481.         flags -- flags on folder/stream to examine
  2482.  
  2483.  Result: count of messages with those attributes set
  2484.   ----------------------------------------------------------------------*/
  2485. long
  2486. count_flagged(stream, flags)
  2487.      MAILSTREAM *stream;
  2488.      char       *flags;
  2489. {
  2490.     char   *flagp;
  2491.     int     old_prefetch;
  2492.     extern  MAILSTREAM *mm_search_stream;
  2493.     extern  long    mm_search_count;
  2494.  
  2495.     mm_search_stream = stream;
  2496.     mm_search_count  = 0L;
  2497.  
  2498.     old_prefetch = (int) mail_parameters(stream, GET_PREFETCH, NULL);
  2499.     (void) mail_parameters(stream, SET_PREFETCH, NULL);
  2500.     mail_search(stream, flagp = cpystr(flags));
  2501.     mail_parameters(stream, SET_PREFETCH, (void *) old_prefetch);
  2502.     fs_give((void **)&flagp);
  2503.  
  2504.     return(mm_search_count);
  2505. }
  2506.  
  2507.  
  2508. /*----------------------------------------------------------------------
  2509.   See if stream can be used for a mailbox name (courtesy of Mark Crispin)
  2510.  
  2511.    Accepts: mailbox name
  2512.             candidate stream
  2513.    Returns: stream if it can be used, else NIL
  2514.  
  2515.   This is called to weed out unnecessary use of c-client streams.
  2516.   ----*/
  2517. MAILSTREAM *
  2518. same_stream(name, stream)
  2519.     char *name;
  2520.     MAILSTREAM *stream;
  2521. {
  2522.     NETMBX     mb_n;
  2523.     char    *s, *t, *u, *host_s;
  2524.  
  2525.     if (stream && (s = stream->mailbox) &&
  2526.                 /* must be a network stream */
  2527.     ((*s == '{') || (*s == '*' && s[1] == '{')) &&
  2528.                 /* must be a network name */
  2529.     ((*(t = name) == '{') || (*name == '*' && (t = name)[1] == '{')) &&
  2530.                 /* name must be valid for that stream */
  2531.     mail_valid_net_parse (t, &mb_n) &&
  2532.     ((!strncmp (mb_n.service, "imap", 4) && 
  2533.                 /* get host name from stream, require imap */
  2534.       stream->dtb && stream->dtb->name &&
  2535.       !strncmp(stream->dtb->name, "imap", 4) &&
  2536.       (host_s = imap_host(stream))) ||
  2537.      (!strncmp (mb_n.service, "nntp", 4) && 
  2538.                 /* get host name from stream, require imap */
  2539.       stream->dtb && stream->dtb->name &&
  2540.       !strncmp(stream->dtb->name, "nntp", 4) &&
  2541.       (host_s = nntp_host(stream->mailbox)))) &&
  2542.                 /* require same host */
  2543.     !strucmp (canonical_name(mb_n.host), host_s) &&
  2544.                 /* if named port, require ports match */
  2545.     (!mb_n.port || mb_n.port == imap_port(stream)) &&
  2546.                 /* require anonymous modes match */
  2547.                 /* debug and bboard flags safely ignored */
  2548.     mb_n.anoflag == stream->anonymous &&
  2549.                 /* if named user, require it match stream's */
  2550.     ((stream->dtb->name[0] == 'i')
  2551.       ? (((u = (char *) mail_parameters(NULL,GET_USERNAMEBUF,NULL)) &&
  2552.           *u && !strcmp(u, imap_user(stream))) ||
  2553.                 /* OR if no named user, require default */
  2554.          (!(u && *u) && !strcmp(ps_global->VAR_USER_ID,imap_user(stream))))
  2555.       : 1))
  2556.       return stream;
  2557.  
  2558.     return NIL;            /* one of the tests failed */
  2559. }
  2560.  
  2561.  
  2562. /* BUG: shouldn't c-client export this? */
  2563. char *
  2564. nntp_host(mbx)
  2565.     char *mbx;
  2566. {
  2567.     NETMBX     mb_n;
  2568.     static char host[256];
  2569.  
  2570.     if(mbx && mail_valid_net_parse (mbx, &mb_n)){
  2571.     strcpy(host, canonical_name(mb_n.host));
  2572.     return(host);
  2573.     }
  2574.  
  2575.     return(NULL);
  2576. }
  2577.  
  2578.  
  2579. /*----------------------------------------------------------------------
  2580.    Give hint about Other command being optional.  Some people get the idea
  2581.    that it is required to use the commands on the 2nd and 3rd keymenus.
  2582.    
  2583.    Args: none
  2584.  
  2585.  Result: message may be printed to status line
  2586.   ----*/
  2587. void
  2588. warn_other_cmds()
  2589. {
  2590.     static int other_cmds = 0;
  2591.  
  2592.     other_cmds++;
  2593.     if(((ps_global->first_time_user || ps_global->show_new_version) &&
  2594.           other_cmds % 3 == 0 && other_cmds < 10) || other_cmds % 20 == 0)
  2595.         q_status_message(SM_ASYNC, 0, 9,
  2596.             "Remember: the \"O\" command is always optional");
  2597. }
  2598.  
  2599.  
  2600. /*----------------------------------------------------------------------
  2601.     Panic pine - call on detected programmatic errors to exit pine
  2602.  
  2603.    Args: message -- message to record in debug file and to be printed for user
  2604.  
  2605.  Result: The various tty modes are restored
  2606.          If debugging is active a core dump will be generated
  2607.          Exits Pine
  2608.  
  2609.   This is also called from imap routines and fs_get and fs_resize.
  2610.   ----*/
  2611. void
  2612. panic(message)
  2613.     char *message;
  2614. {
  2615.     end_screen(NULL);
  2616.     end_keyboard(ps_global != NULL ? F_ON(F_USE_FK,ps_global) : 0);
  2617.     end_tty_driver(ps_global);
  2618.     end_signals(1);
  2619.     if(filter_data_file(0))
  2620.       unlink(filter_data_file(0));
  2621.  
  2622.     dprint(1, (debugfile, "Pine Panic: %s\n", message));
  2623. #ifndef _WINDOWS
  2624.     fprintf(stderr, "\n\nProblem detected: \"%s\".\nPine Exiting.\n",
  2625.             message);
  2626. #else
  2627.     /* fprintf won't do a thing in windows.  Instead, put up a message
  2628.      * box. */
  2629.     sprintf (tmp_20k_buf, "Sorry, Problem detected.  %s.  Very Sorry, pine will now crash, please report this.", message);
  2630.     mswin_messagebox (tmp_20k_buf, 1);
  2631. #endif
  2632.  
  2633. #ifdef DEBUG
  2634.     if(debugfile)
  2635.       save_debug_on_crash(debugfile);
  2636.     coredump();   /*--- If we're debugging get a core dump --*/
  2637. #endif
  2638.  
  2639.     exit(-1);
  2640.     fatal("ffo"); /* BUG -- hack to get fatal out of library in right order*/
  2641. }
  2642.  
  2643.  
  2644.  
  2645. /*----------------------------------------------------------------------
  2646.     Panic pine - call on detected programmatic errors to exit pine, with arg
  2647.  
  2648.   Input: message --  printf styule string for panic message (see above)
  2649.          arg     --  argument for printf string
  2650.  
  2651.  Result: The various tty modes are restored
  2652.          If debugging is active a core dump will be generated
  2653.          Exits Pine
  2654.   ----*/
  2655. void
  2656. panic1(message, arg)
  2657.     char *message;
  2658.     char *arg;
  2659. {
  2660.     char buf[1001];
  2661.     if(strlen(message) > 1000) {
  2662.         panic("Pine paniced. (Reason for panic is too long to tell)");
  2663.     } else {
  2664.         sprintf(buf, message, arg);
  2665.         panic(buf);
  2666.     }
  2667. }
  2668.  
  2669.  
  2670.  
  2671. /*----------------------------------------------------------------------
  2672.   Function used by c-client to save read bytes resulting from a fetch
  2673.  
  2674.   Input: f -- function
  2675.      stream -- stream object to read from
  2676.      size -- number of bytes we're expected to read
  2677.  
  2678.  Result: Alloc'd string containing fetched bytes
  2679.   ----*/
  2680. char *
  2681. pine_gets(f, stream, size)
  2682.     readfn_t       f;
  2683.     void      *stream;
  2684.     unsigned long  size;
  2685. {
  2686.     char      *s, *p;
  2687.     unsigned long  i = 0;
  2688.  
  2689.     s = p = (char *) fs_get ((size_t) size + 1);
  2690.     *s = s[size] = '\0';        /* init in case getbuffer fails */
  2691.     do{
  2692.     (*f)(stream, i = min((unsigned long)MAILTMPLEN, size), p);
  2693.     p += i;
  2694.     gets_bytes += i;
  2695.     }
  2696.     while(size -= i);
  2697.  
  2698.     return(s);
  2699. }
  2700.  
  2701.  
  2702.  
  2703. /*----------------------------------------------------------------------
  2704.   Function to fish the current byte count from a c-client fetch.
  2705.  
  2706.   Input: reset -- flag telling us to reset the count
  2707.  
  2708.  Result: Returns the number of bytes read by the c-client so far
  2709.   ----*/
  2710. unsigned long
  2711. pine_gets_bytes(reset)
  2712.     int reset;
  2713. {
  2714.     if(reset)
  2715.       gets_bytes = 0L;
  2716.  
  2717.     return(gets_bytes);
  2718. }
  2719.